Conversation
main's promote-merge commits (#148, #155) aren't in develop's ancestry, so the next develop→main promote would conflict on release drift again. Trees are already identical (v0.6.0 == develop), so this changes no files — it only brings main's history into develop so future promotes fast-forward cleanly.
…op-2026-07-07 chore: back-merge main → develop (reset promote ancestry after v0.6.0)
test(preflight): pin #152's schema-type-aware label diversity in the parity harness
…+ single-machine CLI scope (#157) Decided by Lukas 2026-07-07. Two coupled decisions: - The CLI is scoped to THIS machine (one client per machine — the §7.2 anchor); the account's fleet lives in the web-UI. So `client use` is removed and `client list` is internal-only (hidden; still callable for the installer pre-flight). Amends the §6.2 surface; folds in the cli#136 client-surface prototype. - Offboarding is a top-level `tracebloc delete` (not `client delete --uninstall`): a SOFT offboard that revokes the machine credential, tears down the local install, wipes local data, and RETAINS all backend artifacts (client record, dataset catalog, use cases, trained models) as history. A hard row-destroy is REJECTED — it would cascade the client's training telemetry (weight refs, confusion matrices, carbon). New §7.10 (design of record) + Rev 9 callouts on §6.2/§7.4; R12 flipped to addressed; C.6 corrected to name the built machine-revoke endpoint (POST /edge-device/<id>/revoke, backend#886) and note DELETE is unrouted. Supersedes the cli#136 proto's client delete/DELETE offboarding draft. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…beddings (#162) (#163) The Schema drift check (scripts/sync-schema.sh --check) was red on every open PR: internal/schema/ingest.v1.json had drifted from upstream tracebloc/data-ingestors@master, which added two task categories (sentence_pair_classification, embeddings), a content_hash data_id strategy, and description updates. - Re-vendor the current upstream schema (sync-schema.sh). - Add both new categories to categoryRegistry as FamilyText (they stage raw .txt from texts/, per data-ingestors conventions.py TEXT_CATEGORIES). - Mark both CLISupported: false with an UnsupportedNote, mirroring causal_language_modeling/seq2seq/token_classification, until the `tracebloc ingest` discover/build staging path for them is confirmed. (Flip to true if the push path already supports them.) - Update the pinned lists in category_registry_test.go. Fixes the registry⇄schema parity test (TestRegistryCoversSchemaCategories) that a bare schema sync would otherwise break. go build/vet/test green. Closes #162 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…ace (§7.10) (#160) * feat(nodeboot): node teardown seam for offboarding (RFC-0001 §7.10) Add internal/nodeboot — the inverse of the installer's node bootstrap that `tracebloc delete` will drive. Based on the cli#136 prototype's k3d/helm teardown, with an injectable Runner exec seam: - ClusterExists(name) / TeardownCluster(name) — `k3d cluster delete` when the cluster exists (also prunes its kubeconfig entry); a missing cluster is an idempotent no-op. - UninstallChart(namespace) — `helm uninstall`, swallowing not-found. - PruneImages() — NEW beyond #136: reclaim tracebloc images, SCOPED to `ghcr.io/tracebloc/*` (docker images -q | docker rmi), deduped, best-effort. NEVER a blanket `docker system prune`. All four are unit-tested against a fake Runner — no real k3d/helm/docker runs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(cli): top-level `tracebloc delete` offboarding (RFC-0001 §7.10) Add `tracebloc delete` — a top-level command (NOT `client delete --uninstall`): on the single-machine CLI the host owns one client, so "delete the client" is "remove tracebloc," and a top-level verb avoids colliding with `data delete`. A SOFT offboard with a removed / retained / left three-way summary shown before a TYPED-CLIENT-NAME confirm (skipped only with --yes). Flow, in order: - guard: refuse if the client is online / has a running job unless --force (reuses the #146 lookupClientStatus / clientStatusOnline); - api.RevokeClient(id) → POST /edge-device/<id>/revoke (NEW; 2xx = revoked) — kills the credential while PRESERVING the row (never a hard destroy that would cascade the retained training history); - nodeboot.UninstallChart → TeardownCluster → PruneImages (best-effort warns); - rm -rf HOST_DATA_DIR (~/.tracebloc) unless --keep-data; - remove the running CLI binary + its sibling `tb` symlink LAST (best-effort; on failure prints the exact rm, or `brew uninstall tracebloc` if brew-managed). Never removes Docker/Homebrew/kubectl/k3d/helm/NVIDIA, never reboots, never `docker system prune`. Flags: --yes / --keep-data / --force + the standard --kubeconfig/--context/--namespace; uses the existing exitError contract. Tests (fake nodeboot Runner + httptest backend + temp HOST_DATA_DIR + temp config dir; no real ~/.tracebloc, k3d/helm/docker): (a) no --yes non-interactive refuses with no side effects; (b) --yes runs the full sequence — revoke POSTed to /edge-device/<id>/revoke, nodeboot fakes called in order, ~/.tracebloc removed; (c) --keep-data spares ~/.tracebloc; (d) running job refuses unless --force; (e) the RETAINED + LEFT copy is present in output. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(cli): withdraw `client use`, hide `client list` (RFC-0001 §7.10) The single-machine CLI owns exactly one client, so there is nothing to select: - Remove `client use` (newClientUseCmd + runClientUse + its AddCommand entry + TestClientUse). The active-client pointer is now set only by `client create`. - Hide `client list` (Hidden: true) — kept callable for the installer's one-client-per-machine pre-flight, off the user-facing surface. - Rewrite the `client` parent help and the root home-screen: drop the `client list` line, add `tracebloc delete`, point offboarding at it. - Retarget the residual "run `client use`" guidance (create save-failure fallback, client status, doctor, clustertarget "runs elsewhere" + multi-namespace refusal) to `client create` / --namespace, and adjust the test that asserted the old string. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(cli): correct data-delete catalog copy — kept, not removed (RFC-0001 §7.10) The catalog metadata for a deleted dataset is NEVER removed — it is kept as a record on tracebloc and marked unavailable (soft-flagged). The old "removed automatically" copy in `data delete` help + its success line, and the push.TeardownPlan doc comment, wrongly implied the backend deletes it. Reword all three to "kept as a record on tracebloc, marked unavailable." Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): redirect the save-failure hint off the removed `client use` Review finding (medium): the client-create config-save fallback still pointed at `tracebloc client use %d`, which T3 removes. Point it at `client create` (idempotent — it re-adopts this cluster's client). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): address delete-offboard review findings (§7.10) Review follow-ups on the tracebloc delete offboarding PR: - api: RevokeClient POSTs to /edge-device/<id>/revoke/ WITH a trailing slash — the DRF DefaultRouter registers the `revoke` @action as /edge-device/<pk>/revoke/, and a slashless body-bearing POST hits Django's APPEND_SLASH (which errors instead of redirecting), aborting the whole offboard at step 1. - cli: a 403 on revoke now speaks in offboard terms ("offboarding requires CLIENT_WRITE") instead of the provisioning copy the shared askAnAdmin hardcoded; askAnAdmin takes action/capability params. - cli: an empty namespace warns + points at --namespace instead of silently skipping the Helm uninstall while the summary claims success. - nodeboot: UninstallChart matches helm's specific "release: not found", not a bare "not found" that could swallow a cluster-unreachable error. - cli: --force help no longer advertises a running-job check the guard doesn't implement. - cli: looksBrewManaged resolves symlinks so the Intel /usr/local/bin → Cellar install gets the `brew uninstall` hint. - config: correct stale `client use` comment. Tests added for each behavioral fix; go build/vet/test green (11 pkgs). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Asad Iqbal <asad.dsoft@gmail.com>
…inter under --keep-data (#165) Two Cursor Bugbot findings on the develop->main PR (#164): - High: `tracebloc delete` registered --kubeconfig/--context but runDelete never read them, so `helm uninstall` ran against the ambient current-context — offboarding could uninstall the wrong release when the operator's context isn't the tracebloc cluster. UninstallChart now takes kubeconfig + kubeContext and appends --kubeconfig/--kube-context (only when set, preserving default-context behavior); runDelete threads the flags through. (k3d cluster delete targets the cluster by name, so it's context-independent.) - Medium: under --keep-data, offboard revoked the credential but left the active-client pointer in ~/.tracebloc, so a later sign-in/reinstall still saw the host as enrolled as the (now-revoked) client. Clear ActiveClientID/Name/Namespace even under --keep-data (token + on-host data stay; re-running `client create` re-adopts by cluster_id), per §7.5. Tests: flags reach helm (unit + delete-level); --keep-data clears the pointer while sparing the data dir. go build/vet/test green (11 pkgs). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…p stale client-list hints (#168) Thorough review pass on the develop->main PR (#164), addressing the new Bugbot finding plus adjacent gaps a review flagged: - Stale client pointer after a failed wipe (Bugbot): after revoke, if `rm -rf ~/.tracebloc` failed the default path left the active-client pointer intact (the --keep-data path cleared it). Now the pointer is cleared in memory once after revoke and persisted in BOTH paths, incl. the wipe-failure branch — the host never looks enrolled under a dead credential. - Misleading success line: the closing "Offboarded … no longer connected" printed unconditionally even when uninstall/teardown/wipe left real state behind (all best-effort warns). Track a `degraded` flag over the state-leaving steps and print an honest closing that tells the user to finish the flagged steps by hand. (Image reclaim is pure disk cleanup — excluded; exit stays 0 since the credential revoke, the core, succeeded.) - Online guard: a stored id not in the signed-in account (found=false) now emits a hint instead of silently passing the guard. - Stale copy: `client status` errors pointed at the now-hidden `client list`; retargeted to `client create` / the installer, matching the phrasing used elsewhere. Fixed a `create/use` comment (use removed). Tests added: wipe-failure clears the pointer; a failed teardown produces the degraded closing. go build/vet/test green (11 pkgs). Rolls up under the §7.10 offboarding work (Bugbot follow-up). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
#166) (#167) The CLI streams a full copy of the dataset into SharedRoot/.tracebloc-staging/<table>, and the in-cluster ingestor COPIES (shutil.copy, never moves) those files into the final table dir. So after a successful `tracebloc data ingest` BOTH copies lived on the shared PVC — the staged source was only ever removed by `data delete` or an `--overwrite` re-ingest. Every successful push of a file-bearing dataset silently doubled PVC usage, eventually surfacing as "no space left on device" on a later ingest with no signal as to why. Add push.CleanStaging: a best-effort rm -rf of ONLY StagedPrefix(table) via the same ephemeral stage-identity pod Teardown already uses (it runs as the uid that wrote the staging files, so the rm works by ownership on hostPath and CSI alike). It never touches the final table dir or the MySQL table. runDataIngest calls it after a CLEAN success only (classifyPushOutcome == "succeeded"), so it never fires on --detach (the Job is still reading the source), completed_with_failures, or any failure. A failed reclaim logs a warning and does not fail the ingest. Robustness (from pre-PR adversarial review, all low-severity): - the reclaim's wait+exec is bounded by StagingCleanupTimeout (45s) so a stuck/unschedulable cleanup pod can't tack the full pod-ready timeout onto a command the user already saw succeed; - the cleanup pod is created under a detached context so a parent-ctx cancel in the create window can't orphan a server-committed pod; - with --output-json the result object is emitted BEFORE the reclaim, so scripted consumers get their result at ingest-completion latency. Longer term the cleaner fix is server-side (ingestor move-not-copy / remove SRC after a verified load) — will file a data-ingestors follow-up. Closes #166. Refs #67. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…save fix(cli): delete guard fails fast on 426; keep-data save failure marks offboard degraded
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d960e01. Configure here.
| if errors.As(lerr, &ue) { | ||
| return &exitError{code: 1, err: lerr} | ||
| } | ||
| p.Hintf("Couldn't check whether this client is still online (%v) — continuing; pass --force to skip this check.", lerr) |
There was a problem hiding this comment.
Delete guard ignores auth errors
Medium Severity
The pre-offboard online check treats every lookupClientStatus error except HTTP 426 as transient, warns, and continues. Revoked or expired credentials surface as 401/403 from ListClients, which will not recover on retry; the command should fail fast with a sign-in prompt like client status, not proceed toward revoke and teardown.
Triggered by learned rule: Poll/retry loops must fail-fast on non-transient API errors
Reviewed by Cursor Bugbot for commit d960e01. Configure here.
…171) `removeSelf` deleted any sibling file named `tb` (guard was only `tb != exe`), with no check that it was tracebloc's own symlink. If a user had an unrelated `tb` on the same PATH dir tracebloc installed into (another tool's binary, or a symlink elsewhere), `tracebloc delete` removed it — wrong-target deletion of a file tracebloc never created. Make delete symmetric with install. The installer refuses to clobber a pre-existing `tb` (install.sh: creates it only when absent or when `readlink tb == PREFIX/tracebloc`). Delete now mirrors that: new aliasStatus() removes `tb` only when it is a symlink resolving to this binary; a regular file or a symlink pointing elsewhere is left in place with a note ("isn't tracebloc's `tb` alias"). Relative targets resolve against the link dir, and an EvalSymlinks fallback covers the case where the binary itself is a symlink (Intel Homebrew). Found in the FR review of the offboarding surface; not a Bugbot finding. Tests: TestDelete_OwnTBAlias_Removed (real tb->binary symlink is removed), TestDelete_ForeignTBAlias_Left (a foreign regular-file `tb` is NOT removed and is reported), and the full-sequence test now lays down a real symlink. The foreign-tb test is verified to fail without the guard. Suite + vet green. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>


Note
Medium Risk
Offboarding is destructive (local data, cluster, credential revoke) with extensive tests, but partial teardown and revoke ordering mean operators must heed degraded warnings; ingest staging cleanup is best-effort and must not run on non-success paths.
Overview
Implements RFC-0001 Rev 9: the CLI is one machine, one client —
client useis removed,client listis hidden (installer-only), and offboarding is top-leveltracebloc delete, notclient delete --uninstall, so it does not collide withdata delete.tracebloc deleteis a soft offboard: typed client-name confirm (or--yes), optional--keep-data/--force, thenPOST /edge-device/<id>/revoke/via newRevokeClient, Helm uninstall,internal/nodebootk3d/helm/docker teardown (scoped image prune), clear active-client pointer, wipe~/.tracebloc, and best-effort removal of the CLI and ownedtbsymlink. Backend history stays; docs describe tombstone/backend follow-ups.Ingest / data UX: after a succeeded push,
push.CleanStagingreclaims.tracebloc-staging/<table>on the PVC (best-effort, bounded timeout);data deletecopy now says catalog metadata is retained and marked unavailable, not auto-removed.Schema / validation: ingest schema adds
sentence_pair_classification,embeddings, andcontent_hashfordata_id; category registry and parity goldens add tabular VARCHAR vs FLOAT label cases; golden generator honors explicit per-caseschema.Home screen, doctor, and cluster-target errors drop
client use/ fleet-list guidance in favor ofclient createandtracebloc delete.Reviewed by Cursor Bugbot for commit a2c5816. Bugbot is set up for automated code reviews on this repo. Configure here.