Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
9c5c75f
refactor(resources): read the envelope constants from the contract, k…
shujaatTracebloc Aug 20, 2026
749ea3a
fix(534): drop the unretried apt-get from a required check (#537)
LukasWodka Aug 20, 2026
96d57d3
feat(resources): stamp `resources set` as a human choice (backend#222…
shujaatTracebloc Aug 20, 2026
c246912
feat(telemetry): the CLI validated events and delivered none (backend…
LukasWodka Aug 20, 2026
858ad3b
feat(telemetry): the CLI now drains the installer's spool too (backen…
LukasWodka Aug 21, 2026
af22d8f
chore(shell): drop the pipe into early-closing readers (backend#2264)…
LukasWodka Aug 21, 2026
10c1583
fix(resources): a same-ceiling `resources set` must not require --yes…
saadqbal Aug 21, 2026
c1e918f
fix(delete): stop the exit-path telemetry write re-creating the wiped…
saadqbal Aug 21, 2026
96fdeac
fix(fmt): the formatter gates walked the working tree, not the repo (…
saadqbal Aug 21, 2026
0617338
fix(cli): resolve the backend env once per invocation, not once per r…
saadqbal Aug 21, 2026
1642780
chore(deps): bump golang.org/x/text in the golang-x group (#530)
dependabot[bot] Aug 22, 2026
e8af6f1
fix(telemetry): the token lookup used a key profiles are not stored u…
LukasWodka Aug 22, 2026
d591943
Merge pull request #540 from tracebloc/release-train/to-staging
tracebloc-release-train[bot] Aug 22, 2026
d02da11
ci(1979): call the board-aware stale sweep instead of copying actions…
LukasWodka Aug 22, 2026
d289cd8
Merge pull request #554 from tracebloc/release-train/to-staging
tracebloc-release-train[bot] Aug 23, 2026
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
9 changes: 9 additions & 0 deletions .cursor/BUGBOT.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,15 @@ Two things make this repo unusual and should shape every finding:
pinned standalone binaries — `errcheck`, `gofmt -s`, `goimports`, `ineffassign`, `misspell`,
`staticcheck`, plus `deadcode-check.sh`, `file-budget.sh`, `check-style.sh`. Don't infer
coverage from that file.
- **The two formatters run via `make fmt-check`, not inline in the workflow** (cli#549), and
they scope to `git ls-files '*.go'` rather than `.`. Both are deliberate: `.` walked untracked
scratch directories, and one definition of the file set is what stops local and CI
disagreeing. `scripts/format.sh` fails closed (exit 2) outside a work tree or on an empty file
list — do not "simplify" either guard away. `run_formatter` returns a status and
writes to a temp file rather than being captured in `$( )`: a function that
`exit`s inside a command substitution ends only the subshell, and the first cut
of this script shipped exactly that false green. `make fmt-selftest`
(`scripts/tests/format-verify.sh`) is the guard; it fails on the old shape.
- **`staticcheck` runs `-checks all,-ST1005` deliberately** — do not flag error-string
capitalisation or punctuation. It is a tracked, intentional exclusion (cli#279).
- `internal/submit/client.go:78` — `InsecureSkipVerify` is intentional for cluster-internal
Expand Down
70 changes: 46 additions & 24 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,48 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: shellcheck + dash parse
# NO `apt-get` (cli#534). Both tools are already on `ubuntu-latest`:
# `shellcheck` is preinstalled -- tracebloc/.github's own `quality / shellcheck`
# job, a REQUIRED check in 16 repos, calls `shellcheck --version` with no
# install at all -- and `dash` IS Ubuntu's `/bin/sh`, an essential package.
#
# WHY IT HAD TO GO, and it is not tidiness. This step is the first thing in a
# REQUIRED check, and `apt-get` here had no retry and no time bound of its own,
# so a slow package mirror consumed the whole 10-minute job budget before any
# shell was parsed. Measured on cli#533 -- a workflow-only diff that cannot
# touch installer behaviour -- which failed FOUR consecutive times:
#
# job 96126585157 Installer (shell) failure 10m16s
# 15:34 shellcheck + dash parse <- 10 minutes here, then killed
#
# Nothing after the `apt-get` line ever ran, and the annotation said
# `Installer (shell)` exceeded 10m -- pointing whoever reads it at the
# installer rather than at package fetching.
#
# Removing the dependency beats hardening it: a step that installs nothing
# cannot stall on a mirror, and no retry/timeout wrapper can say that.
#
# THIS PR'S OWN RUN IS THE PROOF. If either tool were absent the step fails
# loudly on the first line, here, before merge -- which is a better check than
# any claim in this comment.
run: |
sudo apt-get update -qq && sudo apt-get install -y -qq shellcheck dash
shellcheck --version | head -2
shellcheck --shell=sh --severity=error scripts/install.sh
shellcheck --shell=bash --severity=error scripts/check-style.sh
shellcheck --shell=bash --severity=error scripts/check-tool-pins.sh
shellcheck --shell=bash --severity=error scripts/format.sh
shellcheck --shell=bash --severity=error scripts/tests/format-verify.sh
dash -n scripts/install.sh
bash -n scripts/tests/install-verify.sh
shellcheck --shell=bash --severity=error scripts/tests/install-ps1-verify.sh
bash -n scripts/tests/install-ps1-verify.sh
# format.sh's own fail-closed properties. Formatters are stubbed, so this is
# hermetic and needs no Go toolchain — which is why it lives in this job
# rather than Lint. It exists because the first cut of format.sh reported
# "clean" on a formatter that never ran (#550 review).
- name: Formatter-gate harness (fail-closed / tracked-files scope)
run: bash scripts/tests/format-verify.sh

- name: Verification harness (mandatory cosign / fail-closed)
run: bash scripts/tests/install-verify.sh
# Same property on Windows (backend#2078). pwsh is preinstalled on the
Expand Down Expand Up @@ -128,29 +161,18 @@ jobs:
go install github.com/kisielk/errcheck@v1.20.0
errcheck ./...

- name: gofmt -s
run: |
drift="$(gofmt -s -l .)"
if [ -n "$drift" ]; then
echo "::error::gofmt -s drift in:"
echo "$drift" | sed 's/^/ /'
echo "::error::run \`make fmt\` to fix"
exit 1
fi

# goimports -local: enforce the stdlib / third-party / our-own import
# grouping that .golangci.yml's local-prefixes already declares. gofmt
# doesn't check grouping, so drift accumulated silently until now.
- name: goimports -local
run: |
go install golang.org/x/tools/cmd/goimports@v0.48.0
drift="$(goimports -local github.com/tracebloc/cli -l .)"
if [ -n "$drift" ]; then
echo "::error::goimports (import grouping) drift in:"
echo "$drift" | sed 's/^/ /'
echo "::error::run \`make fmt\` to fix"
exit 1
fi
# gofmt -s (simplification) + goimports -local (the stdlib / third-party /
# our-own import grouping that .golangci.yml's local-prefixes declares;
# gofmt does not check grouping).
#
# `make fmt-check`, not an inline copy: both formatters now scope to
# `git ls-files '*.go'` instead of `.` (cli#549), and a second inline copy
# of that scope here is how local and CI start disagreeing about which
# files are gated. It also drops the restated goimports pin — the version
# is declared once, by GOIMPORTS_VERSION in the Makefile, which is what
# check-tool-pins.sh now enforces for this tool too.
- name: gofmt -s + goimports -local
run: make fmt-check

- name: ineffassign
run: |
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ jobs:
# broke the copy assertions without touching delete.go. The glob
# internal/cli/delete*.go deliberately excludes data_delete*.go
# (`tracebloc data delete` is a different command, unit-tested).
#
# internal/cli/telemetry*.go is here for the same reason as
# internal/ui, and backend#2314 is the proof: the command-outcome
# event is emitted from main.go AFTER the offboard returns, and its
# spool lives inside the ~/.tracebloc the offboard just deleted — so
# a telemetry change re-created the wiped tree and broke the teardown
# suite's config-dir assertion without touching delete.go at all.
filters: |
e2e:
- '.github/workflows/e2e.yml'
Expand All @@ -82,6 +89,7 @@ jobs:
- 'cmd/**'
- 'test/integration/**'
- 'internal/cli/delete*.go'
- 'internal/cli/telemetry*.go'
- 'internal/nodeboot/**'
- 'internal/api/**'
- 'internal/config/**'
Expand Down
147 changes: 147 additions & 0 deletions .github/workflows/envelope-contract-drift.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
name: Envelope contract drift (cross-repo)

# internal/resources/envelope_contract.json is VENDORED from
# tracebloc/client-runtime (backend#2220, RFC-BACKEND-664 §P0). client-runtime
# owns the training-envelope arithmetic
# (node_sizing.envelope_from_allocatable); this repo, the bash installer and its
# PowerShell twin are readers of it. Before that consolidation the same policy
# was typed out in all three, none derived from the others — and they disagreed:
# set.go ranked candidate nodes (cpu, memory) while the bash installer ranked
# them (memory, cpu), so on a cluster of 8c/16Gi + 4c/32Gi `resources set` and
# the installer anchored on DIFFERENT nodes.
#
# Unlike the installers, Go needs no generator: the contract is embedded verbatim
# with go:embed, so the vendored artifact is byte-identical to upstream and this
# gate is a plain diff. internal/resources/contract_test.go replays the
# contract's golden vectors through MaxRunCores/MaxRunGiB on every PR; this job
# is the other half — it catches the contract itself going stale.
#
# Pin, don't float (scripts/.client-runtime-ref), exactly as this repo already
# does for tracebloc/client and tracebloc/data-ingestors: an unrelated upstream
# commit must not redden every open CLI PR, and the weekly run catches a pin gone
# stale enough to matter.
#
# FAIL-CLOSED. client-runtime is private, so this needs a token GITHUB_TOKEN
# cannot provide; when it cannot read upstream the job FAILS rather than warning
# and exiting 0. A check that never executed must not report as a passing one —
# the activation-phase fail-open cli#536 had to remove from the backend-fixtures
# gate for exactly this reason.

on:
schedule:
- cron: "0 6 * * 1" # weekly Monday, offset from chart-drift (05:00)
workflow_dispatch:
pull_request:
branches: [develop, main]
paths:
- "internal/resources/**"
- "scripts/.client-runtime-ref"
- ".github/workflows/envelope-contract-drift.yml"

permissions:
contents: read

jobs:
envelope-contract:
timeout-minutes: 10
name: Envelope contract gate (pinned client-runtime ref)
runs-on: ubuntu-latest
steps:
- name: Checkout this CLI ref
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: cli

- name: Resolve the pinned client-runtime ref
id: pin
# First non-comment, non-blank line of scripts/.client-runtime-ref — the
# same convention .client-ref and .data-ingestors-ref use. Shape is
# validated (SHA/branch/tag characters only, no "..") before it reaches
# the checkout action.
run: |
ref="$(grep -vE '^[[:space:]]*(#|$)' cli/scripts/.client-runtime-ref | head -1 | tr -d '[:space:]')"
if [ -z "$ref" ]; then
echo "::error file=scripts/.client-runtime-ref::no ref found — the first non-comment line must be a commit SHA"
exit 1
fi
if ! printf '%s' "$ref" | grep -qE '^[A-Za-z0-9][A-Za-z0-9._/-]*$' || printf '%s' "$ref" | grep -q '\.\.'; then
echo "::error file=scripts/.client-runtime-ref::invalid ref shape: $ref"
exit 1
fi
echo "ref=$ref" >> "$GITHUB_OUTPUT"

- name: Mint a read-only installation token for client-runtime
id: token
# Least privilege per the backend#2157 sweep: named `repositories`, not
# owner-wide, and contents:read only — this job reads two files.
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ secrets.RELEASE_TRAIN_APP_ID }}
private-key: ${{ secrets.RELEASE_TRAIN_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: client-runtime
permission-contents: read

- name: Checkout tracebloc/client-runtime @ pinned ref
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: tracebloc/client-runtime
ref: ${{ steps.pin.outputs.ref }}
token: ${{ steps.token.outputs.token }}
path: client-runtime
persist-credentials: false

- name: The vendored contract matches upstream, byte for byte
env:
PINNED_REF: ${{ steps.pin.outputs.ref }}
run: |
upstream="client-runtime/envelope_contract.json"
vendored="cli/internal/resources/envelope_contract.json"
if [ ! -f "$upstream" ]; then
echo "::error::$upstream is missing at $PINNED_REF — has the contract moved or been renamed?"
exit 1
fi
if ! diff -u "$vendored" "$upstream"; then
echo "::error file=internal/resources/envelope_contract.json::the vendored envelope contract has drifted from tracebloc/client-runtime@$PINNED_REF"
echo ""
echo "To adopt the upstream change:"
echo " 1. cp <client-runtime>/envelope_contract.json internal/resources/"
echo " 2. update the SHA in scripts/.client-runtime-ref"
echo " 3. go test ./internal/resources/... # the golden vectors WILL have moved"
echo ""
echo "If the overhead or the floors moved, that is a FLEET envelope change"
echo "(backend#2167, RFC-BACKEND-664 L0) — not a re-vendor. Say so on the PR."
exit 1
fi
echo "vendored contract matches client-runtime@$PINNED_REF"

- name: Upstream's own goldens are not stale against its own arithmetic
# A vendored contract can match upstream byte-for-byte while UPSTREAM's
# vectors have gone stale against upstream's code — in which case we are
# faithfully mirroring a lie. Re-derive them from client-runtime's own
# generator and require no diff. Pure-python, no cluster, no deps.
run: |
cd client-runtime
if [ ! -f scripts/gen_envelope_vectors.py ]; then
echo "::error::client-runtime@${{ steps.pin.outputs.ref }} has no scripts/gen_envelope_vectors.py — the contract's provenance cannot be verified"
exit 1
fi
python3 scripts/gen_envelope_vectors.py
if ! git diff --exit-code -- envelope_contract.json; then
echo "::error::client-runtime@${{ steps.pin.outputs.ref }} carries goldens that its own generator does not reproduce."
echo "The pinned ref is not self-consistent — fix it upstream, then re-vendor and re-pin here."
exit 1
fi
echo "upstream goldens reproduce from upstream code"

- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: cli/go.mod
cache: true
cache-dependency-path: cli/go.sum

- name: The CLI still agrees with the contract it vendored
run: |
cd cli
go test ./internal/resources/... -run 'Contract|Golden|DecisionA|Overhead|FloorText' -v
33 changes: 33 additions & 0 deletions .github/workflows/stale-backlog-caller.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Close stale backlog issues

# THIN CALLER (backend#1979). The sweep used to be a 16-way copy of
# `actions/stale`, which could not be given board awareness: eligibility has to
# read the card's `Status`, and a script cannot be maintained as sixteen
# byte-identical copies (backend#1597 item 1).
#
# The reusable it calls replaces `actions/stale` with `stale-backlog.py`, whose
# eligibility is exactly `Backlog` — so a `North Stars` epic, or anything already
# in the pipeline, can no longer be auto-closed by a sweep that could not see
# which column it was in.
#
# NO INPUTS PASSED ON PURPOSE. Every input the callee declares is defaulted
# (`project-number: 2`, `dry-run: false`, `strict: false`, `script-ref: main`),
# and a caller may only pass inputs the `@main` callee declares — passing one it
# does not have kills the run at startup_failure, which is why the callee had to
# land on `main` before these callers could be armed at all.
on:
schedule:
- cron: '0 0 * * 1' # Mondays 00:00 UTC
workflow_dispatch: {}

# `contents: read` only. The sweep's writes go through the App token minted
# inside the reusable, not through GITHUB_TOKEN — and asking for more here than
# the callee needs would exceed a minimal grant and fail the run at startup with
# no jobs (the same constraint code-quality.yml documents).
permissions:
contents: read

jobs:
stale:
uses: tracebloc/.github/.github/workflows/stale-backlog.yml@main
secrets: inherit
33 changes: 0 additions & 33 deletions .github/workflows/stale-backlog.yml

This file was deleted.

Loading
Loading