Skip to content

develop -> main#164

Open
saadqbal wants to merge 11 commits into
mainfrom
develop
Open

develop -> main#164
saadqbal wants to merge 11 commits into
mainfrom
develop

Conversation

@saadqbal

@saadqbal saadqbal commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

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 clientclient use is removed, client list is hidden (installer-only), and offboarding is top-level tracebloc delete, not client delete --uninstall, so it does not collide with data delete.

tracebloc delete is a soft offboard: typed client-name confirm (or --yes), optional --keep-data / --force, then POST /edge-device/<id>/revoke/ via new RevokeClient, Helm uninstall, internal/nodeboot k3d/helm/docker teardown (scoped image prune), clear active-client pointer, wipe ~/.tracebloc, and best-effort removal of the CLI and owned tb symlink. Backend history stays; docs describe tombstone/backend follow-ups.

Ingest / data UX: after a succeeded push, push.CleanStaging reclaims .tracebloc-staging/<table> on the PVC (best-effort, bounded timeout); data delete copy now says catalog metadata is retained and marked unavailable, not auto-removed.

Schema / validation: ingest schema adds sentence_pair_classification, embeddings, and content_hash for data_id; category registry and parity goldens add tabular VARCHAR vs FLOAT label cases; golden generator honors explicit per-case schema.

Home screen, doctor, and cluster-target errors drop client use / fleet-list guidance in favor of client create and tracebloc delete.

Reviewed by Cursor Bugbot for commit a2c5816. Bugbot is set up for automated code reviews on this repo. Configure here.

saadqbal and others added 6 commits July 7, 2026 16:50
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>
Comment thread internal/cli/delete.go
Comment thread internal/cli/delete.go
…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>
Comment thread internal/cli/delete.go
saadqbal and others added 2 commits July 7, 2026 18:41
…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>
Comment thread internal/cli/delete.go
Comment thread internal/cli/delete.go
…save

fix(cli): delete guard fails fast on 426; keep-data save failure marks offboard degraded

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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.

Comment thread internal/cli/delete.go
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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants