You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Added
devops sandbox network-policy --allow-collector and sandbox_network_policy(allow_collector=True) add one egress rule to the OTel collector's pods: the otel namespace and the opentelemetry-collector / otel-collector pod labels together, on TCP 4317 and 4318, with no ipBlock. It applies to the sandbox_namespace, public_whitelist and local_whitelist modes; with isolated or bridge the command exits 1 naming those modes (#702).
Sandbox containers and execs carry trace context: with telemetry enabled, devops sandbox deploy, devops docker sandbox and devops test sandbox give the container TRACEPARENT (and TRACESTATE when set) parented to the sandbox.deploy or docker.workload_sandbox.run span, and each devops sandbox exec runs in its own sandbox.exec span and passes that span's context to the command. DockerEngineService.exec_in_container takes environment. The configured env is not changed, and with telemetry disabled the container's environment is exactly the configured one (#702).
devops review score SESSION... --labels FILE [--json] scores saved review sessions against a label file, offline: lenient, strict and per-distinct-defect precision split by producer, VERIFIED and HIGH/CRITICAL precision, recall against the label file's recall set and against its input's known defects, and stability across sessions of one input digest (the mean pairwise Jaccard of their identities and Fleiss' κ of their statuses over the identities every one reported). Every ratio shows its n; a ratio over a row no label covers reads "not computable", and those rows are listed. A SESSION without a readable findings.json is refused, and a session that records no input digest is compared with no other. Each score is kept as a review-score run, which names each figure it could not compute and why (#1138).
A row's identity is its cluster in a session the label file maps row by row; otherwise the fingerprint_v2 of the findings.sarif result at its cited file and line, else that result's tool, rule, path and line or object, and results of more than one tool, rule or fingerprint at that place leave the row unlabelled; otherwise, for a model's row, the one label of its path and origin page within five lines. Nothing is read from a title. Stored absolute paths are made relative to the input's checkout, a row outside the session's reviewed tree counts as false, and a row echoed from a fixture page is no recall (#1138).
tests/fixtures/review_labels/labels.json holds the seed labels of S11 and the ten loop sessions (S12–S21): a row map of all 492 findings.json rows and 169 labelled clusters, transcribed from the loop study. Scored, they give S11 12/51 lenient, 11/51 strict and 4/30 per distinct defect, and the loop 75/441 and 49/441 (#1138).
devops review score --materialise-golden DIR writes the golden set's files into a git repository with one fixed commit, for a path review, and lists the 12 golden defects no tool can express as accepted recall loss. It refuses a DIR that is a file and a golden file under .git/ (#1138).
devops ai runs check takes --max-precision-drop and --max-stability-drop for review score runs, and --max-calls-increase for model calls, a review's cost. A precision or stability figure the baseline recorded and the run could not compute, such as precision over a new unlabelled row, fails the check and reads "not computable"; model calls are judged when both runs record them (#1138).
ADR 0005 records that review findings come from tools and checked facts while models explain and plan, and docs/VISION.md item 8 becomes Evidence-First Code Reviews (#1138).
Dependency: statsmodels 0.15.0, with scipy, pandas, patsy and formulaic, for Fleiss' κ; it is imported only when a score computes κ (#1138).
tools.lock (src/devops_cli/tools_lock/) names every tool devops install-tools installs, at an exact version: kubectl, kustomize, helm, argo, argocd, kubectl-argo-rollouts, trivy, kube-linter, popeye, pluto, k9s, gitleaks 8.30.1 and osv-scanner 2.6.0 as binaries with a SHA-256 per platform (linux and darwin, amd64 and arm64), and Semgrep 1.179.0 and Bandit 1.9.4 as Python tools whose requirements files uv pip compile --generate-hashes writes (#1142).
devops install-tools installs gitleaks, osv-scanner, Semgrep and Bandit. A Python tool installs with uv pip sync --require-hashes into a relocatable virtual environment of its own. uv runs with --no-config, so a uv.toml or [tool.uv] in the current directory cannot change the install, and keeps UV_MALWARE_CHECK, UV_CACHE_DIR and UV_LINK_MODE from the environment (#1142).
devops install-tools --check installs nothing: it lists each tool whose command is not linked at its locked version and exits 1 if there is one (#1142).
profile.json records tools_lock_digest, the digest of the tools lock a review started with, and code_changed_during_run, true when devops-cli's own checkout got a commit or an edit while the review ran; the review then prints a warning (#1142).
Changed
A whitelist entry opens one TCP port: the port it names (scheme://host:port, host:port, [v6]:port), else 443 for a bare host, IP or CIDR or https, and 80 for http. An entry that named no port used to open every port, and a local entry such as a bare private address now opens only 443. An entry with another scheme and no port, a port outside 1-65535, and an entry with a path but no scheme (such as 140.82.112.0/20:8443) are refused with an error naming the entry, and the command exits 1. This is a deliberate break, with no fallback to the old rule (#702).
devops scan complexity, MCP scan_complexity and the inspection hotspots report standard McCabe complexity, as Ruff's C901 counts it: assert, with, conditional expressions and boolean operands add nothing, and a closure counts toward the function that defines it as well as on its own. At a cap of 10 the scan now flags the same 54 functions in src/ that ruff check does, where it flagged 244 (#773).
The structural-invariants pre-commit hook counts nesting as CI does, the function body being depth 1, so 5 nested blocks now fail at commit (#773).
It runs from devops_cli.core.code_metrics, the one copy of the metric, with its cap passed as --max-depth 5, and loads no scanner and no pydantic-ai: a run over one file takes about 0.6 s instead of 8.5 s under the same load.
devops ai spec's indentation rule reads the same metric: an except, match, assert, async for or async with block now opens a level, and a try body no longer does.
devops ai runs check also fails a run whose model calls rose more than its --max-calls-increase (default 20%) over the baseline's, for any two runs that both record calls, review benchmarks included (#1138).
docs/SELF_IMPROVEMENT.md's recall protocol scores its samples with devops review score in place of an inline score_recall.py script. It counts reported rows only: it no longer counts the candidates before re-ranking and verification, so its bar counts a sample that reports a set finding, not one that only found it (#1138).
Each tool version installs under ~/.local/share/devops-cli/tools/<name>/<version>/, and its commands in the target directory link there. Once they do, the tool's other version directories are removed, so a pin bump leaves no old build or virtual environment behind (#1142).
devops install-tools exits 1 when any tool does not install, such as a build refused for its checksum; it used to print the error and exit 0 (#1142).
Post-create installs Semgrep from the tools lock with the other managed tools, in place of its own uv tool install semgrep, so only one Semgrep is on PATH. Its semgrep and pysemgrep both link to the locked environment, because semgrep runs the pysemgrep it finds on PATH, and they replace the links uv tool install left (#1142).
devops install-tools --only-missing installs each tool whose command is not linked at its locked version; it used to skip any tool found on PATH (#1142).
devops install-tools status makes no request. It shows each tool's pinned version and whether its command is at the pin, another path PATH finds or not installed, in place of the latest release (#1142).
Removed
devops_cli.security.structural_invariants, and analyze_file_complexity and FileComplexityReport from devops_cli.security.complexity; devops_cli.core.code_metrics replaces them (#773).
devops install-tools no longer looks up any tool's latest release, and its --version option is gone: the lock is the only version source. validate_version_str and the per-tool download URL constants go with it (#1142).
Fixed
devops branches jira now creates and checks out the feature branch; it always failed on git checkout -b -- <name>. When git refuses the branch, the command prints git's reason and exits 1 instead of a traceback (#900).
kube-linter findings carry their own check, message, remediation, manifest file and object. kube-linter writes Check and Remediation beside Diagnostic, but the adapter read the check from Diagnostic, so every finding was titled [kube-linter-check] K8s Security Lint Warning, and a directory scan located each one at the directory. A finding is now titled [<check>] <message>, so the check is its rule id and SARIF ruleId; its fix is kube-linter's remediation; it is located at <manifest>:<Kind>/<namespace>/<name>, or <Kind>/<name> when the manifest names no namespace (no default is guessed); and its category is the class the check or the message names, else security_misconfiguration. devops k8s lint prints the check in its Title column: the table did not escape its cells, so Rich read [<check>] as a markup tag and dropped it (#1045).
devops scan report keeps every finding and lists findings on different objects as separate rows: the fingerprint and the correlation key include the finding's object when it has one. Over the 7 kube-linter reports of k8s/ at da50329 (4 checks, 3 files) the report kept 1 and now keeps 7 under 4 rules, and two Popeye issues on two resources of one sanitizer were 1 and are now 2. Each --json cluster names its object in symbol, as each finding does. A review keeps two kube-linter checks on one object as two candidates (#1045).
Every symbol-located finding (Trivy, Pluto, Popeye, Dive, kube-linter) gets a new fingerprint and SARIF partialFingerprints value once, so a fingerprint suppression of one must be recorded again. A suppression by rule kube-linter-check, in a scan policy or .devops/review.toml, must now name the check. A finding without a symbol keeps its fingerprint (#1045).
A multi-line result (path:start-end, as Semgrep writes it) reads as its start line instead of a symbol named by the range, and a location of only :<line> reads as that line, so both keep their fingerprints when an edit moves them (#1045).
The knowledge base's RAG article no longer says reviews ground on whatever the index holds: a review asks the index for each page by its file path and symbol names, and for each finding it verifies by the finding's title and description, and writes nothing to it (#1138).
A web_fetch result cut by its budget inside a code block now closes the block before the truncation note and the closing </untrusted_web_page> tag (#1298).
devops docs check validates an option written inside optional-group brackets ([-v <ver>], [--json]) instead of skipping it as a placeholder, so a misspelled option in a synopsis fails the check (#1299).
Security
Sandbox NetworkPolicy egress is scoped per entry and port (#702).devops sandbox network-policy and MCP sandbox_network_policy emit one egress rule per whitelist entry, with that entry's addresses and its one TCP port, so no address is opened on every port or on another entry's port. Before, all whitelist addresses shared one rule with no ports, which opened every port and protocol to them.
A public whitelist CIDR is refused when it overlaps a non-public block, not only when its ends are non-public: is_non_public_ip treats a range as non-public when it overlaps one of pydantic-ai's non-public blocks or a private IPv4 block's NAT64 (64:ff9b::/96) or IPv4-compatible (::/96) image. 8.0.0.0/5 (which holds 10.0.0.0/8), 64:ff9b::800:0/101, ::800:0/101 and 64::/16 are refused; 2606:4700::/48 and 140.82.112.0/20 are still admitted (#702).
FastMCP's DEBUG log of each tool call and prompt request no longer records its arguments. With FASTMCP_LOG_LEVEL=DEBUG, a vault_set call wrote its key_values to stderr; the record now names the tool or prompt and reads <redacted> in place of the arguments (#899).
devops config audit-keys no longer prints a config line it cannot parse, which could hold a secret: its warning names only the file, the error class and the line and column. A file it cannot read or parse is reported as unaudited rather than clean, is listed under unaudited_config_files in --json, and makes the command (and the MCP config_audit_keys tool) fail with exit 1 (#1022).
No tool installs unverified any more. kube-linter installed with no checksum, and trivy, popeye, pluto and k9s installed with only a warning when their checksum file could not be fetched; every build is now checked against the lock's SHA-256 and refused on a mismatch (#1142).
A sandbox workspace can no longer hold the user's SSH agent socket or the X11 socket directory (#1384). devops docker sandbox, devops test sandbox, devops sandbox deploy and the MCP tools docker_sandbox and sandbox_deploy now also refuse a workspace that is, or holds, either of these directories, and the refusal names the workspace and the directory:
the directory of the socket SSH_AUTH_SOCK names, and, when that socket is a symlink, the directory it leads to. This includes VS Code's /tmp/vscode-ssh-auth-*.sock when VS Code sets SSH_AUTH_SOCK;
/tmp/.X11-unix, so a workspace of /tmp is now always refused.
A workspace under either directory is still mounted, and an unset or relative SSH_AUTH_SOCK adds no rule.
mask_secrets masks GitHub user tokens (ghu_), installation tokens (ghs_) and refresh tokens (ghr_) as well as ghp_, gho_ and github_pat_, including the new installation tokens of about 520 characters, which contain dots (Actions' GITHUB_TOKEN among them). Before this change a ghs_ token in a pre-receive hook's message would have reached the release command's error output unmasked. A prefix that follows a letter, a digit or _ is part of a longer word, such as a snake_case test name, and is now left as written for every prefix, so code names are no longer rewritten before review (#1398).