Skip to content

Releases: dan-petty/devops-cli

v0.2.35

Choose a tag to compare

@github-actions github-actions released this 13 Oct 23:57
de09432

Added

  • Active marginalia & epistemic scratchpad (devops ai research annotate):
    • Added file-anchored observation and hypothesis tracking under .data/marginalia/<file_hash>.jsonl.
    • Supported five typed marginalia classifications: ASSUMPTION, HYPOTHESIS, CONTRADICTION, QUESTION, and TODO.
    • Added devops ai research annotate command for recording and viewing marginalia with JSON and YAML formatting support.
    • Automatically injected prior marginalia into devops ai research forage trails and node context.
  • Socratic inquiry & knowledge gap formulator (devops ai research socratic):
    • Added metacognitive knowledge gap and ambiguity analysis for task self-assessment.
    • Implemented structured output models for GAP, AMBIGUITY, and UNDERSPECIFIED_CONTRACT with associated rationales.
    • Added minimal diagnostic probes emitting concise clarifying questions and concrete code lookup instructions bounded by --max-probes.
    • Added devops ai research socratic subcommand supporting table, JSON, YAML, and Markdown formats.
  • Cross-domain analogical pattern retriever (devops ai research analogies):
    • Added multi-vector topological clustering over the Knowledge Base RAG collection to discover cross-domain structural pattern isomorphisms.
    • Implemented CrossDomainAnalogy and AnalogyReport structured output models surfacing source module, target module, shared structural pattern, confidence score, and invariant mechanisms.
    • Added devops ai research analogies CLI subcommand supporting query filtering, source/target domain constraints, and table, JSON, YAML, and Markdown output formats.
  • Living mental model synthesizer & causal graph distiller (devops ai research model):
    • Added progressive synthesis of sprawling research observations into structured YAML mental models under .data/research/mental_model_<topic>.yaml.
    • Implemented finite-state machine transitions (StateTransition), causal directed acyclic graphs (CausalGraph, CausalNode, CausalEdge), and verifiable invariant rules (InvariantRule).
    • Added devops ai research model command supporting progressive revision updates, --observations, --file, --show, and YAML, JSON, Markdown, and table formatting.
  • Kubernetes orphaned volume reclamation (devops k8s volume reclaim):
    • Added devops k8s volume reclaim <name> to safely reclaim persistent volumes stuck in Released or Failed phase.
    • Implemented pre-mutation verification confirming volumes are genuinely orphaned: phase check (Released or Failed), claim absence check, and pod mount check across all namespaces.
    • Added confirmation gating requiring --yes in non-interactive sessions, and request-free --dry-run simulation displaying exact API operations.
  • Standardized output format adoption (--format, -f supporting table, json, yaml, csv) across all 144 CLI value commands via emit_serialized and resolve_format (#411).
  • Structured property-based fuzzing test suites for boundary resilience across SARIF 2.1.0 parsing, git worktree list --porcelain, git status --porcelain=v1 cache parsing, Argo CD drift analysis, and review JSON schema normalization (#416).
  • OCI container image packaging & standardized metadata validation (devops devcontainer validate):
    • Added --oci-labels and --expected-source options to devops devcontainer validate to structurally audit OCI annotations prior to container publication.
    • Implemented multi-platform Buildx compilation (linux/amd64, linux/arm64) with QEMU in container release workflows.
    • Added SLSA provenance attestations and SBOM generation to devcontainer and service image packaging pipelines.
  • devops ai gateway render: render and apply LLM gateway configuration derived from Ollama profiles and service aliases, aligning max_input_tokens with tier context lengths, removing deprecated weight fields under least-busy routing, and outputting unified diffs (#478).
  • devops roadmap reprioritize: record a durable tally of joins, descopes and trims on each item's job record, record release start size history, report release growth across releases, emit telemetry gauges, and visualize in Grafana (#1126).
  • Deploy single-binary Grafana Tempo (grafana-community/tempo 3.1.0) in the otel namespace backed by a 10Gi local-path PersistentVolumeClaim (Prune=false,Delete=false) with 168h (7-day) trace retention, Argo CD Application k8s/argocd/apps/tempo.yaml, Alloy integrations.tempo metrics scraping, and Grafana Tempo datasource (uid: tempo) linked to loki (tracesToLogsV2, lokiSearch) and pyroscope (tracesToProfiles) (#1135).
  • Add TempoConfig (settings.tempo), devops k8s tempo, MCP tool k8s_tempo_info, built-in AI tool k8s_tempo_status, and Tempo OTLP v2 trace normalization (normalize_tempo_spans, query_tempo_trace) for devops telemetry profile and devops sandbox traces (#1135).

Changed

  • Container manifests & release workflows:
    • Standardized OCI Image Spec labels across Dockerfile, .devcontainer/Dockerfile, and .github/workflows/release.yml.
    • Pinned uv to 0.12.23 across devcontainer and production Dockerfile builds.
  • Route OpenTelemetry Collector trace exports to otlp_grpc/tempo (tempo.otel.svc.cluster.local:4317) and update k8s/monitoring/dashboards/otel-collector.json to query exporter="otlp_grpc/tempo" and the tempo Grafana datasource (#1135).

Removed

  • Remove the ephemeral jaeger Deployment and Service (k8s/otel/jaeger.yaml), settings.jaeger, devops k8s jaeger, k8s_jaeger_info, and k8s_jaeger_status; on clusters not managed by Argo CD, remove the old resources with kubectl delete deploy,svc -n otel jaeger (#1135).

Fixed

  • Repository-relative static scanner finding paths:
    • Relativized Semgrep and Bandit static scanner findings against the scan root and review tree, eliminating absolute path leaks in candidate locations and descriptions.
    • Normalized candidate findings during the review pipeline's static-scan and admission stages so findings in findings.json and review.md consistently display canonical repository-relative paths (app/x.py:12).
  • PR review post without reporting (devops review pr --post --no-reporting):
    • Ensured PR review comments and findings telemetry metrics are built from verified findings even when the consolidated reporting stage is skipped via --no-reporting.
    • Preserved specific persona labels on review findings across metrics reporting in FINDINGS_TOTAL rather than falling back all findings to the default persona.
  • devops roadmap reprioritize: the release-start trim and the cap descope pass over an item that a staying item depends on, re-evaluating dependencies after each trim, so a release never keeps an item whose open dependency it moved (#1123).

v0.2.34

Choose a tag to compare

@github-actions github-actions released this 11 Oct 21:10
6e862cd

Added

  • Agentic information foraging & scent trails (devops ai research forage):
    • Implemented Pirolli & Card's Information Foraging Theory with a deterministic, local-only crawler exploring codebases via relational semantic scent.
    • Added multi-edge extractors across Python AST symbol imports (ast), internal Markdown references (markdown-it-py), and git co-change history (GitPython).
    • Added best-first heuristic search with heapq and cycle-detection ledger, scoring edges via BM25 lexical similarity (ai/lexical.py) and backtracking when information scent drops below threshold.
    • Registered devops ai research forage CLI command with interactive summary panels, detailed trail tables, and structured --json output.
  • Defect PR hardening audit and post-merge intake automation:
    • Implemented devops pr hardening-report [--pr N] [--fail-on-missing] to audit merged defect pull requests for guardrail additions to declared hardening surfaces (AGENTS.md, src/devops_cli/ai/tasks/*.md, src/devops_cli/ai/knowledge_base/, common_hallucinations.json), explicitly excluding docs/ROADMAP.md.
    • Added reusable defect PR classifier (classify_defect_pr, is_defect_pr) in src/devops_cli/github/defect_classifier.py identifying defect pull requests by type/bug, type/security labels or Conventional Commits fix(...) / fix: titles.
    • Added post-merge audit workflow .github/workflows/defect-pr-hardening-audit.yml triggering on closed merged defect PRs, running the hardening audit with --fail-on-missing, and automatically handing findings to roadmap intake on failure with skip-not-fail for fork PRs.
    • Added workflow contract tests in tests/test_workflow_contracts.py validating triggers, permissions, step commands, intake routing, and static Typer command resolution.
  • Worktree inventory & safe orphan reclamation (devops repos worktrees):
    • Added devops repos worktrees status command to list every git worktree in the repository with per-entry commits ahead of main, dirty-path count, whether any other local or remote ref contains the tip, and git's locked and prunable status.
    • Added --json output option emitting typed arrays containing ahead (int), dirty (int), contained (bool), locked (bool), and prunable (bool).
    • Added devops repos worktrees prune with safe dry-run preview by default; without --force, removes only clean and contained worktrees and refuses unmerged, dirty, or locked checkouts.
    • Added --force support to remove worktrees regardless of clean or unmerged status, cleaning up metadata with git worktree prune.
    • Implemented NUL-separated porcelain parsing via git worktree list --porcelain -z per the #1440 convention to safely support worktree paths with newlines.
  • AGENTS.md Context Budget Enforced (src/devops_cli/config/constants.py):
    • Declared CONST_AGENTS_MD_MAX_CHARS: Final[int] = 40_000 defining the maximum character budget for AGENTS.md.
    • Added unit test in tests/test_agents_md_claims.py verifying that AGENTS.md length is within CONST_AGENTS_MD_MAX_CHARS and CONST_AGENTS_MD_MAX_CHARS <= 40_000.
    • Added @AGENTS.md import directive generation to generate_pointer_stub in src/devops_cli/ai/instruction_generator.py for Claude Code memory loading.
  • Podman engine discovery & rootless sandbox user mapping (devops docker, devops sandbox):
    • Implemented automatic container engine discovery across CONTAINER_HOST, DOCKER_HOST, Docker contexts, rootless Podman ($XDG_RUNTIME_DIR/podman/podman.sock and /run/user/<uid>/podman/podman.sock), rootful Podman (/run/podman/podman.sock), and Docker default (/var/run/docker.sock).
    • Added fast configuration error when CONTAINER_CONNECTION is detected, guiding configuration to CONTAINER_HOST.
    • Added Engine API facts introspection (kind(), is_rootless(), security_options()) to identify Podman and rootless execution models.
    • Added user namespace mapping (resolve_sandbox_user) configuring user="0:0" on rootless engines so sandboxed workloads run with container root mapped to the host user, enabling write access to mounted workspaces without permission errors.
    • Enhanced Docker default socket connection failure message with Podman user socket activation instructions (systemctl --user enable --now podman.socket).
    • Handled Podman's BuildKit absence in devops docker cache with informative guidance directing users to devops docker prune.
    • Propagated resolved DOCKER_HOST to Dive container analysis and granted XDG_RUNTIME_DIR pass-through to Cosign image verification.

Changed

  • Poetically Concise AGENTS.md:
    • Condensed AGENTS.md from 116,655 characters (26,283 cl100k tokens, 497 lines) to 38,355 characters (8,839 cl100k tokens, 221 lines), achieving a 67.1% character reduction and 66.4% token reduction under the 40,000-character budget with 1,645 characters of headroom.
    • Linked operational procedures (release choreography, PR monitoring, review feedback loops) directly to canonical documentation (docs/SDLC.md, docs/SELF_IMPROVEMENT.md).
    • Replaced duplicate rules enforced by named checks with direct pointers to the enforcing commands and pre-commit hooks (uv run devops ci, ruff-check, structural-invariants).
    • Updated CLAUDE.md to load @AGENTS.md using Claude Code memory import syntax.
  • The roadmap service is suspended (k8s/devops/roadmap-service): GitHub suspended the Service's machine account, so deployment/roadmap-service asks for 0 replicas, and an Argo CD sync or devops k8s deploy-stack --stack devops no longer starts it. AGENTS.md and k8s/README.md open their roadmap-service sections with the suspension: nobody scales, restarts or re-pushes secrets for the Service, only a person runs mutating devops roadmap commands, and agents leave the Service's items labelled status/blocked alone.

Removed

  • Unread syntopical and cognitive research settings:
    • Removed dead constants and defaults (DEFAULT_RESEARCH_DIR, DEFAULT_SYNTOPICAL_TIMEOUT_SECONDS, DEFAULT_SYNTOPICAL_MAX_SOURCES, CONST_RESEARCH_DIR_NAME, CONST_DEFAULT_MAX_SYNTOPICAL_SOURCES, CONST_SYNTOPICAL_MIN_RELEVANCE, CONST_SYNTOPICAL_MAX_EXCERPT_CHARS) in config/defaults.py and config/constants.py with no consumers in src/ or tests/.
  • Removed vLLM deployments, configuration, and leftover policies:
    • Removed dead vLLM cluster URL defaults (DEFAULT_VLLM_16GIB_CLUSTER_URL, DEFAULT_VLLM_48GIB_CLUSTER_URL), settings field AIConfig.vllm_url, router method GatewayRouter.scale_vllm, CLI command devops ai gateway scale, and FastMCP tool ai_vllm_scale.
    • Removed vestigial vLLM Kubernetes manifests, perimeter policies, and port 8000 egress rules across k8s/llm/ and k8s/monitoring/, including k8s/llm/profiles/networkpolicy.yaml (vllm-profiles-perimeter) and Portkey vllm-backend.
    • Removed vLLM server busy-share and waiting queue metrics from devops ai gateway load and pool load metrics calculation.
    • Updated .devcontainer/devcontainer.json Ollama endpoint to http://ollama-16gib.llm.svc.cluster.local:11434.
  • Legacy segment review engine & whole-file reviewer:
    • Deleted legacy segment review engine and whole-file reviewer in ai/review/runner.py, leaving ReviewPipelineOrchestrator as the sole review pipeline.
    • Deleted dead _run_persona_loop, segment chunking, recompose functions, and _save_findings_json from ai/review/runner.py.
    • Deleted obsolete task prompts (compose.md, paginated_review_protocol.md, path_review_prompt.md, review_output_instruction.md).
    • Removed compose task: ai.tasks.compose configuration field, AI_TASK_COMPOSE_* settings, DEVOPS_CLI_AI_TASK_COMPOSE_* environment variables, ReviewClients.compose, and PersonaDefinition.compose_prompt.

Fixed

  • AI model pricing snapshot merging, version invalidation, and tiered request pricing:
    • Merged remote and custom partial pricing snapshots field-by-field onto bundled genai-prices dataset instead of replacing entire catalog.
    • Preserved token extraction for both unbundled and partial bundled providers after snapshot ingestion.
    • Recorded installed genai_prices_version and UTC fetched_at in snapshot metadata, falling back to bundled data with warning if versions mismatch.
    • Preserved recorded fetch timestamp as auto_update_timestamp on DataSnapshot.
    • Added --reset option to devops ai cost update-pricing to clear snapshot and caches, with mutual exclusion against --source.
    • Added --format yaml support, base-tier rate column headers, and table captions to devops ai cost list-pricing.
    • Calculated tiered request costs dynamically in SpendGuard.record_usage and EmbeddingResult.cost fallback, while honoring flat-rate caller pricing.
  • Cloudflare tunnel route synchronization and Kubernetes URL setup preservation:
    • Preserved per-route originRequest configuration (noTLSVerify, httpHostHeader), custom catch-all ingress rules, and warp-routing settings during devops cloudflare tunnel sync.
    • Aborted tunnel sync on GET read failure without dispatching PUT updates, avoiding full config replacement and loss of existing routes.
    • Ensured devops cloudflare tunnel sync --dry-run performs zero network requests while outputting serialized GET and PUT request payloads.
    • Matched AI gateway ingress backends via CONST_K8S_INGRESS_SERVICE_PATTERNS using exact service-name and namespace matching rather than substring searches.
    • Protected pre-configured remote custom ai.gateway_url endpoints from being overwritten by discovered FQDNs in _should_update_url.
    • Guarded valkey host and port configuration from being overwritten when configured with a non-default port.
  • Review early exits & zero-model routing (devops review):
    • Exited --summary before `ReviewPipelineOrches...
Read more

v0.2.33

Choose a tag to compare

@github-actions github-actions released this 10 Oct 14:24
69c99cf

Added

  • Commands with a dry-run mode in the ai, argo, devcontainer, docker, docs, grafana, k8s, prometheus, release, repos, review, ssh, telemetry, tf, tls and vault groups now declare --dry-run, among them devops tf apply, devops k8s configure-urls, devops release tag and devops vault logout, so devops <command> --dry-run runs that mode where it used to fail with No such option: --dry-run (#412). Most of these commands print their preview and stop; devops ai analyze path, branch and pr still analyse, without the model, and print the metadata they would save, and devops argo rollouts analyze still evaluates its metric gate but does not abort the rollout.
  • devops release sync-notes --dry-run prints the repository and the releases it would republish, then stops without reading any release from GitHub. With --all it names <all published releases> instead of listing them (#412).
  • CI's static job uploads the SARIF of devops scan report to code scanning under category /devops-scan on every push and pull request, so each scanner's findings show on pull requests and close once fixed (#419).
  • devops review path, devops review branch and devops review pr run mypy over the reviewed Python files and report its errors as findings (#873). mypy runs on the main worktree's .venv Python, or on devops-cli's own interpreter when the main worktree has none, with the reviewed tree's pyproject.toml, or else the main worktree's, as its config file. It runs only when the reviewed tree and the main worktree hold the same uv.lock, because a review never runs uv sync on a reviewed tree, so a project without a uv.lock gets no mypy findings. A branch or pull request review lists every mypy error in a reviewed file as introduced, since it does not type-check the base. The reviews also run Ruff under the reviewed tree's Ruff configuration, but no Ruff finding reaches review.md or findings.json yet: Ruff names each file by a file:// URI, which the review does not match to a reviewed file. Both tools, like the complexity check below, run even when a review is given --no-static-scan, and neither is among the report's listed static analyzers, so the report does not say when one was skipped or could not run.
  • .devops/review.toml takes [advisory] rules, the Ruff rules a review adds to the project's own selection, and defaults to BLE001 (#873). Like Ruff's other findings, theirs do not reach review.md yet.
  • review.md gains a ## Complexity table of each function in the reviewed Python files whose McCabe complexity differs from the base, with its complexity before and after, — standing for a function that was added or removed (#873). Functions are matched by bare name within a file, so of several sharing a name, such as two classes' __init__, only the last is compared. The session's findings.json records the same rows as complexity_delta. A path review has no base, so it lists every function as added. A changed or added function that ends above [complexity] max_complexity in .devops/review.toml (default 10) is a MEDIUM finding, or HIGH above one and a half times that cap, and [complexity] max_increase, when set, also flags any rise of at least that much.
  • tests/test_swallowed_failures_ratchet.py fails when a file under src/ holds more swallowed failures than its ceiling in tests/fixtures/swallowed_failures_ceilings.json, where a file not listed has a ceiling of 0 (#1347). It counts Ruff's BLE001, S110 and S112 findings, and each call passing check=False that stands alone as a statement or is assigned to a name never returned, yielded or read for .returncode, .stdout or .stderr. The failure lists every site in each file over its ceiling as file:line: rule and says to handle the failures rather than raise the ceiling.
  • A file under its ceiling also fails the test, which prints the command that locks the gain in, uv run python tests/test_swallowed_failures_ratchet.py --lower (#1347). That command recounts src/, lowers each such file's ceiling to its count, drops a file whose count reaches 0, and exits 1 without writing when any file is over its ceiling.
  • devops roadmap return <item> hands a roadmap item back to refinement: with --confirm it posts an evidence comment on the issue and sets the item's Status on the board to New (#1348). The comment comes from --comment (-c), or from --comment-file, which replaces --comment when both are given. The command exits 1 on an empty comment, a missing comment file or an item that is not on the board. Without --confirm it prints the item's number and title, the target status and the comment, and writes nothing. --dry-run makes no request and prints the requests a run would make, and --dry-run with --confirm is refused.
  • AGENTS.md asks for a feasibility check before any code or tests: the agent confirms the item's premise against the real system (upstream documentation, library source, live cluster state or the CLI at runtime) and records how in the task file's **Feasibility** field (#1348). When an item cannot be built as specified, or needs a refactor beyond its scope, the agent writes no compensating code: it comments on the issue with the evidence and a proposed re-scope, returns the item to New with devops roadmap return, and stops. Four signals call for stopping instead of adding code: a swallowed error, product code that detects tests, a second fix on top of a fix in the same area within one release, and a review loop that keeps finding new defects in the same feature.
  • .github/roadmap.toml takes release_slots, the items a release holds above release_cap before a start trims it or a joining critical fix descopes one. It defaults to 4, and a negative value fails with an error naming the key (#1514).

Changed

  • devops --dry-run <command>, like a run with DEVOPS_CLI_DRY_RUN exported, now runs the command with --dry-run when the command it resolves to declares that option, instead of printing the generic Would run delegated command: line (#412). Unless the command line already gives --dry-run, it is added directly after the command path, ahead of the command's options, positional arguments and any --. A command that does not declare --dry-run still prints only the generic line and does not run, -h or --help shows the command's help, and devops --help says that a delegated command previews only if it declares --dry-run.
  • devops --dry-run <command>, or a run with DEVOPS_CLI_DRY_RUN exported, now fails with a usage error and exit code 2 on an unknown subcommand instead of printing the generic line and exiting 0 (#412). So does a command that declares --dry-run given an option or argument it rejects, or missing a required one, such as devops --dry-run release prepare without a version.
  • docs/SDLC.md requires state-mutating commands to declare --dry-run instead of using @dry_run_command (#412).
  • devops review verify records a person's verdict as a label on the session's findings.json and candidates.json and nothing else; it ranks review history. A known false positive is a reviewed [[suppressions]] entry in .devops/review.toml with a reason and an expiry, and the six Semgrep false positives people had judged in the learned catalog are now such entries (#1150).
  • Branch and pull request reviews and devops pr check-readiness list the unexpired .devops/review.toml suppressions whose path the change touches, in place of the mitigations ledger's perimeter warning (#1150).
  • A persona's finding is reported unverified until a person judges it; --no-persona-review gives a tools-only review until the persona stage is removed (#1151). devops ai prompt-eval counts the verdicts the feedback dataset records per labeller, since the deterministic layer it replayed is gone (#1150).
  • When devops ai agents has a model write AGENTS.md, in any repository, its prompt asks for a feasibility check before coding and for stopping with an evidence comment instead of adding compensating code (#1348). Its built-in template, used with --template or when no model answers, adds the feasibility check, the hand-back with devops roadmap return and the four stop signals only to the AGENTS.md it writes for devops-cli itself.
  • The task-file template in docs/agent/tasks/README.md has a **Feasibility** line, and tests/test_agent_task_files.py fails any task file without one (#1348). Task files that predate the rule are exempt and are listed in tests/fixtures/task_files_baseline.txt.
  • A release cut with no changelog fragment and no entries under its version's CHANGELOG.md section fails before it pushes, naming the version, its release branch, the missing section and, from devops roadmap close or the roadmap Service, the completed items with no fragment (#1450).
  • devops pr check-readiness lets a pull request into a cut release branch edit CHANGELOG.md once the branch holds its version's section, which is how a critical fix after the cut adds its entry (#1450). A pull request into a release branch that changes CHANGELOG.md before the cut is still blocked, docs/ROADMAP.md stays the cut's, and a failed read of the branch's CHANGELOG.md is a blocker naming the error.
  • devops roadmap close --plan lists CHANGELOG.md among the cut's files and the fragments the cut collects (#1450).
  • devops roadmap reprioritize still fills a starting release to release_cap (12), but trims it only above its limit, release_cap + release_slots (16), so a P0 or P1 item placed in a release of 12 no longer moves another out (#1514). A critical fix that joins a started or cut release descopes its lowest-ranked unstarted item only once the release holds more than 16, and the size check descopes above the larger of 16 and the release's size at start. The cap, over_size and trim reason comments n...
Read more

v0.2.32

Choose a tag to compare

@github-actions github-actions released this 09 Oct 18:43
b5ea6ff

Added

  • Pull requests show changed-line coverage against their base branch (diff-cover), plus any added pragma: no cover lines, in the Tests & Coverage job summary. The report is advisory and sets no coverage threshold (#850).
  • pytest-randomly 5.0.0 joins the dev dependency group. addopts blocks it with -p no:randomly, so pytest, devops ci and every other run keep file order, and uv run pytest --co -q collects the same order as before (#852).
  • devops ci test --repeat N --seed S runs the selected test files N times, one run after another on one process (-n 0), each shuffled by pytest-randomly with seeds S to S+N-1. S defaults to a seed derived from HEAD's commit hash. The first failing run stops the command and prints its seed with the command that reproduces it. --repeat refuses a run that no test file narrows, since shuffling the whole suite waits for thread-leak isolation (#852).
  • The advisory Changed-Test Independence pull-request job in ci.yml runs devops ci test --repeat 3 over the pull request's added or modified test files, and skips its toolchain and test steps when no test file changed (#852).
  • devops ci mutate [PATHS] [--changed] [--base REF] mutation-tests functions with mutmut 3.8 on demand (#853). With --changed it maps the functions the working tree changed since its merge base with --base (default main), untracked files git does not ignore included, to mutmut's names, and a --base naming no commit is an error; PATHS alone select every function in those files. mutmut runs the tests covering each function in its own process, with xdist off, and the command shows each mutant no test killed, with its diff, then the killed, survived, timeout and no-tests counts. It prints no score, exits 0 whatever survives, and is neither a devops ci stage nor a ci.yml step. The first run mutates the whole source tree into the git-ignored mutants/ and runs the test suite once, serially; later runs reuse it, and rm -rf mutants resets it.
  • An architectural invariant fails on any # pragma: no mutate under src, so no line exempts itself from mutation (#853).

Changed

  • The settings parser refuses scheme-less URLs for argocd.url, grafana.url, ai.ollama_urls, and OLLAMA_BASE_URL with a prescriptive ConfigurationError instead of prefixing http://.
  • The network probe refuses targets lacking a host name or carrying invalid ports with a ValidationError.
  • The embedding benchmark refuses unrecognised endpoints under a gateway provider, requiring --provider to benchmark external endpoints.
  • Sandbox whitelist parsing refuses entries containing empty port suffixes (example.com:, example.com:/path).
  • Failed repository clones wrap gitlib.GitCommandError into GitOperationError and report masked stderr from git and OpenSSH without exposing credentials.
  • Pinned GitHub host keys are seeded across all clone operations, including HTTPS clones.
  • The release no longer pins, bumps or pre-publishes the Service image's tag; Argo CD Image Updater rolls the Service onto service:latest (#1486). A release pull request's CI now builds, smoke-tests and scans the Service image without pushing it. On the merge to main, release.yml builds the image, scans it, pushes it by digest and attests its provenance, then tags it vX.Y.Z and latest, and Image Updater moves Application devops onto the new digest within one poll, with no git commit.

Removed

  • In-process known_hosts parser and runtime ssh-keyscan invocation, delegating host key validation to OpenSSH.
  • The release cut's bump of the Service image's newTag and its rewrite of the Argo CD git sources' targetRevision (#1486).
  • devops release check's checks of the Service image pin and of the Argo CD targetRevisions (#1486).
  • The images: pin in k8s/devops/kustomization.yaml (#1486).
  • Service image publishing from release/vX.Y.Z in release.yml, with its publication plan and the step that pointed latest at an image the release branch published (#1486).

Fixed

  • The devops review command tests no longer start the Logfire SDK's token-check thread, which printed the "Logfire API is unreachable" warning on every run and made the dry-run socket-recorder test flaky. The tests read the per-test config instead of a MagicMock settings object, and a background thread that dies with an exception now fails the test run (#965).
  • The ollama tier DaemonSets no longer run ollama as root: every tier runs it as UID and GID 10001 with RuntimeDefault seccomp, no privilege escalation, all capabilities dropped and a read-only root filesystem, with /tmp on an emptyDir (#1060). HOME is /home/ollama, so the key and models stay on the hostPath /var/lib/ollama and nothing is downloaded again. A root initContainer, own-model-directory, holding CAP_CHOWN alone, gives that directory to UID 10001 at every start, because fsGroup does not apply to hostPath volumes; it is the one run-as-non-root exception kube-linter reports, recorded in the manifest and k8s/README.md. test_every_ollama_tier_runs_unprivileged pins these settings for all 9 tiers.
  • Kubernetes service, node, LoadBalancer, and telemetry collector URL builders bracket IPv6 host addresses using httpx2.URL, preventing URL parser failures.
  • configure-urls handles IPv6 cluster endpoints and falls back safely to loopback when endpoints are unreachable, including port-80 LoadBalancers.
  • Kubernetes Service proxy addressing validates namespace, service, and port identifiers and refuses dot segments (.., ., %2e%2e), preventing path traversal and credential leakage beyond the Service boundary.
  • Target resolution for Kubernetes API proxies preserves server path prefixes, raw URI encodings, and cleanly separates connection base URLs and path prefixes.
  • Sandbox network probe targets using https:// are probed over HTTPS with paths appended via append_path, preserving path queries and IPv6 bracketing.
  • URL, endpoint, and authority reads across benchmark runners, docs ingester, telemetry exporters, vulnerability lookups, and pricing now use sending client parsers (httpx2.URL and read_url_or_authority), preventing scheme sniffing and port mismatch bugs.
  • Benchmark key trust boundaries compare canonical URL origins through httpx2.Origin, preventing trust boundary leaks and ensuring consistent host evaluation.
  • The documentation crawler normalizes link resolution and origin checks using httpx2.URL.origin, preventing scheme downgrades and SSRF boundary escapes.
  • OTLP telemetry client preserves explicit collector schemes and endpoints, preventing invalid gRPC port conversions and TLS configuration errors.
  • Vault broker URI parsing respects scheme case-insensitivity and preserves fragment separation.
  • Prometheus keeps every series node-exporter serves (no allow list; its collector flags decide what is collected), so Node Exporter Full and the Kubernetes node dashboard show data again (#1129).
  • The alloy-singleton collector runs as one Deployment pod through the chart's singleton preset instead of a DaemonSet per node, so Kubernetes events are watched and shipped once (#1129).
  • Alloy writes the cluster name to Prometheus once, as cluster, without the duplicate k8s_cluster_name label (#1129).
  • kube_hpa_labels is removed from the kube-state-metrics allow list, since kube-state-metrics v2 never serves it (#1129).
  • CI jobs name their runner image, ubuntu-24.04, instead of ubuntu-latest, and uv in ci.yml and release.yml uses only its own CPython builds (UV_MANAGED_PYTHON). GitHub moves ubuntu-latest to Ubuntu 26.04 from 2026-10-19, where uv would have used the image's own Python 3.14 instead of the 3.14.7 that .python-version pins. The move to ubuntu-26.04 waits for the workflow linter to accept the label (#1501) (#1494).
  • CI's and the release's Service image scans run Trivy 0.75.0, the version tools.lock pins for devops install-tools, instead of 0.70.0, and a workflow contract test fails whenever an aquasecurity/trivy-action step's version differs from the lock (#1499).

Security

  • The host sandbox no longer declares a noexec /tmp that bubblewrap never applied (#798). Bubblewrap has no noexec option for a tmpfs, so the host policy now declares only the 64 MiB size, and the container sandboxes keep their size=64m,noexec policy. A host sandbox whose policy declares a tmpfs option bubblewrap cannot apply, such as noexec or size=50%, now fails at construction with an error naming the option, instead of dropping it. SECURITY.md now says that the host sandbox's /tmp allows execution.
  • The output-cap test now runs a writer that ignores SIGPIPE and checks that the sandbox kills it at the cap within 1 s, well before its timeout (#798).
  • GitHub clones append GitHub's published host keys directly to ~/.ssh/known_hosts, ensuring junk known_hosts lines no longer skip pinning and failed keyscans no longer skip host-key verification silently.

v0.2.31

Choose a tag to compare

@github-actions github-actions released this 09 Oct 13:58
27cf6bd

Added

  • deptry quality gate step enforcing that pyproject.toml declares all directly imported dependencies and flags unused or transitive packages (#1120).
  • Argo CD Image Updater keeps the devops Application on the digest ghcr.io/dan-petty/devops-cli/service:latest points at. Application argocd-image-updater installs the argo-helm chart pinned to 1.3.1 in namespace argocd, and its one ImageUpdater (k8s/argocd/image-updater-values.yaml) writes service:latest@sha256:<digest> into the Application's spec.source.kustomize.images within one 2-minute poll of latest moving, with no git write-back. roadmap-service and CronJob devops-cli roll onto a release once release.yml points latest at it, and the newTag pin in k8s/devops/kustomization.yaml no longer decides the running image. k8s/README.md shows how to read the deployed digest (#1485).

Changed

  • Test worker startup latency and test collection time reduced by ~25% (saving 9.50s on 16 workers, and 11.27s on the full test run) by deferring heavy LLM and vector client SDK imports (anthropic, google.genai, kubernetes, qdrant_client, openai) across devops_cli.ai agents, models, and provider packages until first use (#1338).

Removed

  • Timeout re-export removed from devops_cli.ai.settings and devops_cli.ai.agents; create_model_settings and resolve_runtime_model_settings accept numeric seconds as float | None (#1120).
  • Unauthenticated Git daemon service and port 9418:
    • Removed the background Git daemon service stub and its step from devops devcontainer post-start, eliminating unauthenticated serving of repository clones under repos/ on port 9418 (#1122).
    • Dropped DEVOPS_GIT_DAEMON_AUTOSTART and DEVOPS_GIT_DAEMON_PATHS configuration environment variables (#1122).
    • Dropped port 9418 forwarding and attributes from .devcontainer/devcontainer.json and the devcontainer template, and removed port 9418 egress from Argo CD network policy (#1122).

Fixed

  • The durable-run store (durable_runs.db), Kubernetes port-forward daemon state file (k8s/port_forwards.json), and docs-ingestion directory now resolve their default paths through resolve_store_path under the repository data directory, respecting DEVOPS_CLI_DATA_DIR and data.dir settings instead of resolving relative to the working directory. An architectural invariant prevents direct .data path construction across src/ (#1036).
  • Test consoles render CLI help at the conftest width (COLUMNS=250) in every environment by preventing Typer's GitHub Actions detection from forcing an 80-column terminal under TERM=dumb, eliminating width-dependent test discrepancies between local runs and GitHub Actions CI (#1041).
  • devops docker push now supports custom registry endpoints with port specifications (e.g. localhost:5000/app:1.0, example.com:443/app:1.0), delegating repository validation directly to docker-py and the daemon while safely escaping progress output (#1108).
  • Prevent credential leakage in SSRFBlockedError details by masking single target URLs before bounding string length, eliminating legacy string partitioning (#1109).
  • Ensure deduplication and correlation produce identical finding sets and cluster sequences regardless of input order across all permutations (#1141).
  • Enforce complete linkage in correlation clustering so findings only join when correlating against every cluster member, leaving 2 clusters for bridging-first chains (#1141).
  • Select cluster representatives and scalars deterministically using highest severity, narrowest parsed region span, and total finding key, sorting locations numerically (a.py:9 before a.py:10) (#1141).
  • Tests touch no state outside temporary directories: each pytest worker process runs with an isolated home under pytest basetemp before devops-cli is first imported, with a git identity on example.com (#1311).
  • TLS test passes --tls-dir, user data root resolves through DEVOPS_CLI_USER_DATA_ROOT across git/worktree.py, core/repo.py, and tools_lock, and worktree tests assert paths stay under tmp_path (#1311).
  • The Ollama round-robin index lives under DEVOPS_CLI_DATA_DIR instead of the system temp directory, and tempfile.gettempdir() resolves under tmp_path for every test (#1311).
  • The floor's patches of CONFIG_PATH and CONST_USER_DATA_ROOT in tests/conftest.py are removed (#1311).
  • The session workspace tripwire verifies that worker homes leave no state at devops-cli's locations (CONST_CONFIG_DIR, CONST_USER_DATA_ROOT, DEFAULT_SSH_KEY_DIR, DEFAULT_LOCAL_BIN_DIR) and fails the session naming each leaked path (#1311).
  • k8s/otel/networkpolicy.yaml rule 4 now admits Traefik on TCP 8888, allowing the otel-metrics-ingress route to reach the OpenTelemetry collector through Traefik (#1371).
  • devops k8s deploy-stack now deploys both argocd/networkpolicy.yaml and otel/networkpolicy.yaml natively under the infra stack manifests, removing both exemptions from the undeployed network policy guard suite (#1371).
  • Docker sandbox and test sandbox dry runs validate the workspace:
    • devops docker sandbox --dry-run and devops test sandbox --dry-run now validate the workspace directory via WorkloadSandboxRunner.build_dry_run_details(), refusing forbidden roots, home directories, socket directories, and sensitive paths with DockerSandboxError before executing any commands or container engine calls (#1383).
    • Preserved request-free dry-run behavior for remote tcp engine hosts (DOCKER_HOST) without DNS egress lookups or container client dialing (#1383).

Security

  • Finding fingerprint and run-store digest regression pins:
    • Pinned finding fingerprints (NormalizedFinding.fingerprint) across multiple scanners, symbols, and canonicalisation formats to literal 32-character SHA-256 hashes, ensuring algorithm changes cannot silently invalidate suppression policies (#855).
    • Pinned CONST_SARIF_FINGERPRINT_KEY to "devopsCli/v1" with regression tests enforcing version bumps on any algorithm changes (#855).
    • Added golden OASIS SARIF 2.1.0 report (tests/golden/sarif_report.json) validated against schema and asserted against SARIF emission output (#855).
    • Pinned run-store content digests (run_store.digest) to literal 16-character SHA-256 hashes for primitive and nested payloads, preventing baseline key orphaning (#855).
  • The Docker daemon endpoint check now covers the exact endpoint the SDK dials, resolving endpoints in priority order across DOCKER_HOST, DOCKER_CONTEXT, currentContext in config.json, and the default socket. Uppercase schemes, bare address-and-port endpoints (e.g. 192.0.2.1:2375), and Docker-context TCP endpoints are now validated against SSRF egress policies, requiring ai.allow_private_network for private network targets. Missing contexts now fail fast with an informative error instead of silently falling back to the default socket (#1108).
  • Standardize URL credential masking and redaction on library parsers (urllib.parse and httpx2), covering arbitrary schemes (postgres://, redis://, mysql://, custom://, http://, https://), passwords containing @, #, or /, and empty-username URLs (redis://<masked-user>:<masked-password>@..., http://<masked-user>:<masked-password>@...) while keeping IPv6 host brackets intact and preserving original http:// schemes without rewriting (#1109).
  • Introduce the <masked-url> fail-closed placeholder for whole URLs that cannot be parsed safely or contain anomalous userinfo delimiters (#1109).
  • Sanitize OpenTelemetry collector endpoints via strict parsing and IP classification, masking private, CGNAT, link-local, and documentation IPs to the URL-safe internal-ip placeholder (#1109).
  • Strip userinfo credentials from gateway served_by headers (x-litellm-model-api-base) across both blocking and streaming LLM responses before storing into the spend ledger, event payloads, and trace span attributes (#1109).
  • Migrate devops scan and NormalizedFinding to Fingerprint v2 (devopsCli/v2), hashing tool, rule ID, repository-relative path, enclosing qualified symbol from AST, and a 100-character non-whitespace window starting from the flagged line, with occurrence indexing for collisions (#1141).
  • Identify region-less findings via SARIF logicalLocations (kind/namespace/name[/container] for Kubernetes resources, purl@version for dependency advisories) (#1141).
  • Dropped the 0.0.0.0/0 ingress peer from k8s/argocd/networkpolicy.yaml and k8s/otel/networkpolicy.yaml, admitting Traefik by its selector instead of world CIDRs that admitted every pod in the cluster under kube-router. Argo CD's API-server egress now admits TCP 6443 through an ipBlock peer with cloud metadata SSRF protection, allowing GitOps reconciliation against the Kubernetes API server (#1371).

v0.2.30

Choose a tag to compare

@github-actions github-actions released this 09 Oct 05:47
02736de

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: ...
Read more

v0.2.29

Choose a tag to compare

@github-actions github-actions released this 08 Oct 21:48
af0a642

Added

  • devops vault logout: revokes the token devops vault login stored, at the address and namespace that issued it (auth/token/revoke-self), then deletes it from the OS keyring. The local copy is deleted even when Vault is unreachable or the token has expired, and the output says the revoke did not happen (#709).

Changed

  • A token an earlier vault login stored as a bare string is no longer used; run devops vault login again (#709).
  • devops gh secrets sync --source vault --dry-run no longer reads Vault: it lists each secret it would read, seal and upload, so the dry run sends no request (#709).
  • devops gh project sync and devops gh project reconcile add no card: an issue reaches the board through devops roadmap intake, and a pull request gets no card, its progress showing on its issue's card. New pull requests no longer get board cards, and sync no longer adds the repository's open issues. Reconcile reports how many open issues wait for intake (#892).
  • devops gh project reconcile --plan reads the board and the repository and lists the changes; --dry-run, and the global dry run, make no request and list the requests a run makes. MCP gh_project_reconcile takes mode (dry-run, plan by default, or write) in place of dry_run, so a call with no arguments no longer writes (#892).
  • devops gh project sync reports the fields it created and reconcile's outcome, prints reconcile's stop line and exits 1 when reconcile stops early or does not start, and its dry run makes no request and prints no board number (#878, #892).
  • Every devops gh project and devops gh views --template option defaults to .github/project-template.json at the repository root, so the commands work from a subdirectory; reconcile --project-number reads the board's own Status options and loads no template (#1010, #892).
  • DEVOPS_CLI_AI_ALLOW_PRIVATE_NETWORK is read only through ai.allow_private_network, when a client is built: validate_url, the LLM client and the HTTP broker no longer parse the variable themselves, and the broker no longer freezes it at import. A check made with allow_private=False stays public-only, and an AIConfig built in code (an agent given a model name, model_request_sync, the chaos fallback, GatewayRouter() with no config) takes its own allow_private_network rather than the variable (#898).
  • new_http_client and new_async_http_client take a required level and own verify, http2 and limits; tenacity retries are opt-in with retries=, because the retry transport turns every 4xx and 5xx into an error. HttpClientBroker keeps one client per level, and request, arequest, new_client, get_client and get_async_client take the level; every Vault request (vault_request) uses the private level. get_shared_client and get_shared_async_client take the level, which is part of the pool key, so a connection never crosses levels (#898).
  • The LLM client builds its clients at the configured level, loopback or private when ai.allow_private_network is set, and raises when one cannot be built instead of falling back to a client without the retry transport (#898).
  • The broker's request hooks check only a request's own egress policy and the scheme; the async hook no longer runs a blocking DNS lookup on the event loop (#898).
  • httpcore2 and anyio are declared dependencies, at the versions already locked, and h11, which the test fakes use to write and read HTTP/1.1, is a declared dev dependency (#898).
  • A release whose pull request has merged, and whose GitHub Release is not published yet, is now merged (ReleaseState.MERGED) instead of cut, as the reprioritize plan's "Current release" line shows. A critical fix filed then goes first into the next release, with a "the release pull request of vX.Y.Z has merged" comment, and the start of a release a person cut early, its pull request open or merged, still pulls nothing in (#1294).
  • plan_intake and intake_candidate take the repository they run on (repo), which devops roadmap intake and the Service's intake job pass (#1358).
  • The intake --dry-run request plan lists the .github/labels.yml read first among the candidate reads, before the timeline reads and the embedding call, with the condition that the run has a candidate; for a --title candidate it has no condition (#1358).
  • devops roadmap intake --limit N decides the N oldest candidates and lists those it left; without it intake decides every candidate, as before, and its --dry-run still makes no request (#1360).
  • The reprioritize row's webhook hints are milestone closed and release published (CONST_ROADMAP_REPRIORITIZE_BATCH_KEYS); any other milestone event is no longer one (#1360).
  • ChangeKind.RELEASE_STARTED, which nothing recorded, is removed, and IssueRecord carries the issue's updated_at (#1360).
  • RoadmapStore.add_item returns the Item it added, Item and BoardEntry carry the card's node id (card_id), and changes_since takes except_actor, an empty one leaving out nothing. The run record card is written by the id gh project item-create --format json returns, with no board read after the create (#1361).
  • The --dry-run request plans list each board write's card read and its item-edit by node ids, and the board's fields and each filter's listing once a run; intake's placement lists no board read after its add (#1361).
  • require_budget, require_write_floor and require_floor put the budget's reset in their error's details (reset_at, ISO 8601), and refusal_reset reads it back (#1400).
  • A roadmap store that has sent no GraphQL request reports no spend and sends no budget query (#1400).
    • A devops roadmap command whose store sent none ends with no GraphQL line.
    • The --dry-run plans list the closing budget read only for a run that sent a GraphQL request.
    • RoadmapStore.graphql_spend(read=False) reports from the budgets already held and sends nothing.
  • LocalDurabilityCapability is built on pydantic-ai's public durable engine API: an engine_spec and a backend that runs each operation in-process. Toolsets are not wrapped, and model requests go through pydantic-ai's durable model in the same process. An event_stream_handler must be async, as pydantic-ai's EventStreamHandler type requires. pydantic-ai now rejects a second durability capability on the same agent (#1446).
  • A PydanticAgent tool that takes a context must annotate it with pydantic-ai's RunContext. A tool annotated with devops_cli.ai.agents.context.RunContext now fails schema generation, because the private pydantic-ai hook that accepted the subclass no longer exists. Use isinstance to narrow to the subclass when the tool reads loaded_capability_ids or tool_call_approved (#1446).
  • calculate_usage_cost prices through genai_prices.calc_price and returns its PriceCalculation, or None when genai-prices cannot price the model or usage. get_active_traceparent reads the active span through OpenTelemetry's W3C trace-context propagator (#1446).

Removed

  • The devops_cli_llm_cache_hits, devops_cli_llm_cache_misses and devops_cli_llm_cache_writes gauges (#816). Each sent 1.0 from a short-lived process and counted nothing; devops_cli_ai_requests_total's cached label carries hits.
  • devops gh project sync --task-file, the task-file parser (parse_tasks_to_project_items, ProjectItem) and sync's invented item counts (#878, #892).
  • DEFAULT_GH_GRAPHQL_SAFETY_THRESHOLD; DEFAULT_GH_GRAPHQL_BUDGET_FLOOR is the one GraphQL threshold (#892).
  • The broker's per-request allow_private_network override, its allow_private_networks argument and HttpClientBroker.validate_url; ALLOW_PRIVATE_NETWORK_ENV in devops_cli.ai.client and CONST_AI_ALLOW_PRIVATE_NETWORK_ENV; the Jaeger and probe resolve-and-check copies (_is_unsafe_ip, _resolve_safe_jaeger_target, _resolve_safe_socket_addr, _is_blocked_metadata_host). connection_limits moved from devops_cli.http.pool to devops_cli.http.client (#898).
  • devops_cli.ai, devops_cli.ai.agents, devops_cli.ai.agents.pydantic_agent, devops_cli.ai.result and devops_cli.ai.run no longer re-export these pydantic-ai internals: best_effort_price (removed from pydantic-ai), OutputSchema, OutputValidator, TextOutputSchema, run_image_process_hooks, run_output_with_hooks, SyncStreamBridge and current_otel_traceparent (#1446).

Fixed

  • Vault commands use the token devops vault login stores: the broker resolves its token from the argument, then the stored login (only for the Vault address and namespace that issued it), then VAULT_TOKEN, then DEVOPS_CLI_VAULT_TOKEN, through the shared resolver, so devops vault audit records each lookup. vault status shows a Token row naming the source, or the address a stored token that was passed over was issued for, and the dry runs of get, set, sync and leases name the token source (#709).
  • vault set, vault sync and every vault leases mode exit 1 before any request when no token is usable, naming devops vault login, VAULT_TOKEN and DEVOPS_CLI_VAULT_TOKEN. When Vault answers 401 or 403, commands exit 1 naming the token's source and the status, with no keyring fallback. A refused vault leases --revoke prints the status Vault answered (#709).
  • vault get answers from the OS keyring only with no usable token, when Vault has no value at the path, or when Vault is unreachable, and says which source answered; the not-found message names only the sources checked. vault sync, devops gh secrets sync --source vault and the shared secret chain read Vault alone, and vault sync of a path with no secret exits 1 instead of reporting zero secrets (#709).
  • vault login --store exits 1 before logging in when no encrypted, unlocked OS keyring can keep the token, instead of reporting success with the token kept only in memory, and a Vault r...
Read more

v0.2.28

Choose a tag to compare

@github-actions github-actions released this 07 Oct 23:44
910b823

Added

  • Every reported scanner finding and dependency advisory is admitted with its producer's anchor (ToolAnchor or AdvisoryAnchor) and a location validated against the reviewed commit in devops_cli.review.admission.admit(), writing findings.sarif and rendering introduced, pre-existing, and suppressed findings in review.md, one suppressed row per file and line (only scanner findings are compared with the base revision; every dependency advisory is listed as introduced), with admission rejections counted by type in profile.json; an inline marker suppresses only findings of its own tool (nosec for Bandit, nosemgrep or no-semgrep for Semgrep, noqa for Ruff), and findings from the review's models are not admitted (#871, #1295, #1341).
  • Increase in-cluster roadmap-service analysis task context window in k8s/devops/configmap.example.yaml from 16k (16384) to 64k (65536) tokens (#1271).

Changed

  • Unify pull request check runs and commit status contexts retrieval into a single fail-closed reader supporting combined statuses across >30 contexts and reporting zero checks as pending across pr wait, monitor, and readiness (#769).

Fixed

  • Defend dashboard domain panels against mounting race conditions by deferring snapshot rendering when unmounted or during child query races, preventing NoMatches worker failures (#519).
  • Isolate MCP in-process child process stdio to temporary files to prevent protocol stream corruption, return bounded masked failure output with FastMCP ToolError on non-zero exit codes, and expand ci_run tool to accept any gate check name with budgeted timeout (#839).
  • Ensure web_fetch reports failed HTTP fetches as tool failures (ToolFailed) and security blocks as errors, rather than returning failure descriptions as page text (#894).
  • Align all hand-written documentation (README.md, ARCHITECTURE.md, RELEASE_CYCLE.md, docs/SDLC.md, docs/ROUTINE_TASKS.md, docs/DEVCONTAINER_USAGE.md, docs/VISION.md, k8s/README.md, and k8s/llm/profiles/README.md) to name only commands, options, flags, and architecture patterns that exist, backed by static argv validation in devops docs check (#921).
  • Reviews of branches and pull requests scan an isolated detached worktree of the reviewed revision under user data root instead of the active checkout, preventing uncommitted edits from leaking into review findings or symbol metadata (#1047).
  • Git change set parsing uses git diff --name-status -M to ensure deleted files are never passed to static analyzers, and renames track the target path at head while preserving old paths (#1047).
  • Single-file path reviews resolve repository root via git rev-parse --show-toplevel, and uncommitted reviews report scanning in place (#1047).
  • Pre-analysis of the worktree reuses cached metadata when content hash matches the commit blob, bypassing mtime checks (#1047).
  • PR reviews fetch head from refs/pull/N/head and mark partial context when unavailable (#1047).
  • Path classes loaded from .devops/review.toml route fixtures and golden sets to secret scanning only, skipping general analyzers (#1047).
  • Detailed per-tool coverage matrix recorded in profile.json and rendered under ## Coverage in review.md (#1047).
  • Gitleaks regex fallback deleted in favor of deterministic binary execution; missing binary reports as not installed (#1047).
  • Ensure a review verifier verdict that both confirms and refutes its finding stays unverified with a verifier-contradiction note rather than ending VERIFIED or verifier-inconclusive, even when its reason repeats the title or confirms the claim (#1059).
  • A failing embedding model pauses RAG lookups with a circuit breaker after three consecutive transient errors instead of permanently ending RAG for the run, while permanent errors from unknown or unserved models stop RAG with one warning (#1065).
  • Route lookups concurrently once healthy while coordinating a single probe after failures, avoiding serialized wait loops across concurrent review workers (#1065).
  • Hash prompt-prefixed text for L1 and Valkey L2 embedding cache keys, supporting distinct model prompt prefixes across qwen3-embedding, bge-m3, nomic, and e5 families (#1065).
  • Pin CONST_KNOWN_EMBEDDING_DIMENSIONS["qwen3-embedding"] to 1024, honor configured ai.rag.top_k and ai.rag.score_threshold (including threshold 0), skip package manager lockfiles, and bound semantic chunks to 8,192 estimated tokens, which is 32,768 characters at the project's four-characters-per-token estimate (#1065, #1296).
  • End failed indexing batches with exit 1 naming the first and last file. A run without --force resumes where it failed: a file is cached once all of its chunks are stored and as incomplete until then, so a file that spanned the failed batch is embedded again, and its stored chunks are purged if it is deleted or edited first (#1065, #1296).
  • Route review requests through the LLM gateway with least-busy routing and cancel_on_disconnect: true, avoiding edge cut stalls and backend queue build-up on Ollama (#1069).
  • devops ai gateway connect automatically discovers and configures the gateway's NodePort address on the LAN, probing /health/liveliness and /model/info while bypassing edge timeouts (#1069).
  • Enforce single retry layer across LLM client transports: retryable status codes (408, 429, 5xx) retry with exponential backoff while non-retryable 4xx errors fail immediately without retry loops (#1069).
  • Lower a gateway review's file workers from 16 to three, the sum of DEFAULT_GATEWAY_REVIEW_SLOTS, or to ai.ollama_max_parallel per ai.ollama_urls entry when that is more. Each file's personas still query the gateway in parallel, so requests in flight can exceed the slot count. Isolate persona retry execution, and purge expired or CLOSE-WAIT connections from the HTTP connection pool (#1069).
  • Track main across all Argo CD git-source targetRevision fields under k8s/argocd/ while retaining the release version pin for the service image in k8s/devops/kustomization.yaml (#1261).
  • Fix Portkey AI Gateway liveness and readiness probe path to / and align GPU Feature Discovery image to nvcr.io/nvidia/k8s-device-plugin:v0.16.2 (#1267).
  • Disable ambient Logfire pytest plugins in test runner configuration to prevent network DNS lookups in dry-run tests (#1267).
  • Resolve Argo CD application synchronization and manifest generation failures: specify valid port number: 3100 for loki-ingress, deduplicate Grafana dashboard ConfigMap ownership between base and monitoring, remove redundant Service/pyroscope alias colliding with the Pyroscope Helm chart, and remove the homelab domain from the committed overlays; a native deploy-stack --stack infra applies k8s/monitoring/dashboards itself (#1279, #1297).
  • Supply the homelab's values to Argo CD at deploy time instead of from the repository (#1290):
    • devops k8s deploy-stack renders ConfigMap devops-cli-config from config.yaml, outside any Application.
    • It writes the hosts each of the Applications devops and ingress renders, at the revision Argo CD builds (or --argocd-revision), onto the Application as Kustomize patches. The cluster app-of-apps ignores those patches.
    • It renders everything before it writes, and refuses an unset service setting or domain by name.
    • teardown-stack deletes the ConfigMap, and the repository no longer tracks k8s/devops/configmap.yaml.

Security

  • Redact secret-bearing argv items from async subprocess telemetry command summaries and terminate the entire POSIX process group on subprocess timeouts (#780).
  • Render untrusted web pages as escaped markdown within context budget with dynamic backtick fencing, chrome/dialog decomposition, prompt boundary tag sanitization, and response URL provenance isolation (#896).

v0.2.27

Choose a tag to compare

@github-actions github-actions released this 06 Oct 16:29
e61ae46

Added

  • Kubernetes deployment health diagnosis (devops k8s doctor):
    • Add devops k8s doctor command and k8s_doctor FastMCP tool to diagnose cluster deployment health and correlate symptoms across nodes, pods, and warning events (#408).
    • Attribute workload symptoms to cluster-level root causes, absorbing resident pods into node findings and identifying stuck PersistentVolume reclaims (#408).
    • Support --namespace, --context, --tail, --format table|json|yaml, and --dry-run execution with deterministic ranking and secret masking (#408).
  • Argo CD GitOps cluster state reconciliation:
    • Introduce a two-level root Application topology (bootstrap tracking main and cluster tracking the active release branch) managing 20 leaf Applications (8 raw manifests and 12 multi-source Helm charts) (#755).
    • Establish homelab and homelab-system AppProjects with cluster-scoped kind whitelisting and orphaned resource tracking (#755).
    • Add declarative domain overlays under k8s/overlays/homelab/ replacing templated overlays and standardizing ingress patching (#755).
    • Add monitoring/grafana-admin secret to keyring push table with live credential adoption (#755).
  • Sequential pipeline stage context budgeting:
    • MultiAgentPipeline allocates carried outputs from prior stages newest-first up to DEFAULT_PIPELINE_STAGE_CONTEXT_TOKENS (4,096 tokens, overridable via --stage-context-tokens or constructor), listing older stages that get no budget by name only and appending ScratchpadBuffer.render_context_summary() once after carried outputs outside the budget (#859).
    • PipelineStepResult records context_tokens (exact token count of carried context sent to the stage) and context_truncated (#859).
  • devops pr update --dispatch-ci flag:
    • Add --dispatch-ci to devops pr update to poll the head branch SHA and trigger ci.yml via GitHub Actions workflow dispatch after updating branches (#984).
    • Grant actions: write permission exclusively to update-pull-requests in .github/workflows/update-prs.yml and add architectural invariants gating Actions write permissions across all workflows (#984).
  • Pinned invariants survive memory summarization, lead every system prompt and recur on a cadence:
    • Partition AgentMemory with an invariants store that survives extractive and LLM summarization as well as clear() operations (#985).
    • Prepend non-negotiable operational rules verbatim at the head of every system prompt, recur compressed reminders on a 3-turn cadence using stdlib textwrap.shorten, and seed devops ai chat sessions with DEFAULT_CHAT_INVARIANTS (#985).
  • OpenTelemetry tracing for background service and Kubernetes workloads:
    • Enable OpenTelemetry export in k8s/devops/configmap.yaml targeting the in-cluster collector at http://otel-collector-opentelemetry-collector.otel.svc.cluster.local:4318 (#1197).
    • Add egress rule to k8s/devops/networkpolicy.yaml (devops-default-perimeter) permitting TCP ports 4318 and 4317 to namespace otel for all current and future workloads in namespace devops (#1197).
    • Configure OTEL_SERVICE_NAME: roadmap-service in k8s/devops/roadmap-service/deployment.yaml (#1197).
    • Instrument create_service_app with HTTP request tracing and timing middleware (skipping trace spans on /healthz, /readyz, /metrics probes) and trace batch job executions in RepoWorker._execute_batch (#1197).
  • DevOps Stack in deploy-stack & stack all:
    • Add "devops" stack to devops k8s deploy-stack and automatically include it in devops k8s deploy-stack --stack all, ensuring all in-cluster manifests (networkpolicy.yaml, serviceaccount.yaml, configmap.yaml, cronjob.yaml, and roadmap-service) and secrets are deployed and kept up to date (#1225).
    • Pre-register namespace devops in k8s/namespaces.yaml with restricted pod-security standards and ArgoCD Prune=false annotations (#1225).
    • Add automated rollout restart of deploy/roadmap-service in namespace devops during stack deployment to apply updated ConfigMap values immediately (#1225).
    • Update AGENTS.md instructions with the architectural requirement that all in-cluster Kubernetes resources must have a managed stack path in deploy-stack --stack all (#1225).
  • GitHub Traffic Metrics & Automated Telemetry Ingestion in Roadmap Service:
    • Add GitHub repository traffic and engagement telemetry instruments (devops_cli_project_traffic_views_total, devops_cli_project_traffic_views_uniques_total, devops_cli_project_traffic_clones_total, devops_cli_project_traffic_clones_uniques_total, devops_cli_project_traffic_referrers_total, devops_cli_project_traffic_paths_total, devops_cli_project_stars_total, and devops_cli_project_forks_total) (#1227).
    • Implement record_project_metrics_in_registry to populate in-memory metrics registry for direct Prometheus scraping via roadmap-service:8000/metrics (#1227).
    • Add scheduled metrics job to DEFAULT_DUE_TABLE in roadmap-service running immediately on startup and every 15 minutes to continuously refresh repository velocity and traffic metrics (#1227).
    • Add GitHub Traffic & Repository Engagement section with stat and bargauge panels to the Project & Engineering Velocity Grafana dashboard (k8s/monitoring/dashboards/project-metrics.json) (#1227).
    • Add traffic and referral summaries to devops gh metrics CLI output (#1227).
  • Dynamic DevOps ConfigMap Generation & Sanitized Template:
    • Add k8s/devops/configmap.example.yaml template with sanitized placeholder repositories and account name (#1231).
    • Add /k8s/devops/configmap.yaml to .gitignore and untrack it from version control to prevent user repos and bot accounts from leaking (#1231).
    • Add devops_cli.k8s.configmap module with ensure_devops_configmap and render_devops_configmap_content to dynamically materialize and synchronize the cluster ConfigMap from active configuration (service.repos, service.machine_account, or k8s.github_account) (#1231).
    • Update devops k8s deploy-stack and devops k8s render to ensure configmap.yaml is generated from active settings before applying manifests (#1231).
  • GitHub & Cloudflare Published Service Status Monitoring:
    • Add devops gh status, devops cloudflare service-status, and unified devops status commands to monitor published upstream platform operational status, active incidents, and component health (#1235).
    • Add Statuspage domain models (StatuspageSummary, StatuspageComponent, StatuspageStatus, StatuspageIncident) and telemetry client in devops_cli.telemetry.service_status (#1235).
    • Register devops_cli_upstream_service_status and devops_cli_upstream_component_status OpenTelemetry/Prometheus instruments, recorded continuously by the metrics adapter in devops roadmap run (#1235).
    • Integrate published service status into Grafana dashboards: stat panel on ingress-tunnel.json (Cloudflare Service Status) and dedicated row with GitHub and Cloudflare status panels on project-metrics.json (#1235).
  • Keep issues with open or merged pull requests in active releases:
    • Update roadmap store pull request queries (open_pull_requests) to index both OPEN and MERGED states via GitHub GraphQL and in-memory store (#1244).
    • Add Event.PR_JOINED and Reason.PULL_REQUEST transitions so items with in-flight or merged pull requests joining a started or cut release are admitted and kept rather than demoted to the backlog (#1244).
    • Guard release start and descoping rules so items with pull requests remain in the starting release even if New or Blocked, and are excluded from over-cap descoping victims (#1244).

Changed

  • Stack lifecycle and release management delegation:
    • Update devops k8s deploy-stack to detect Argo CD-managed clusters, run secret push, and delegate manifest and Helm reconciliation to GitOps (#755).
    • Guard devops k8s teardown-stack against manual execution when Argo CD manages the cluster (#755).
    • Pin Helm release versions across all stack manifests and CLI upgrade commands (#755).
    • Automate Argo CD git targetRevision rewriting to the next open roadmap release during release prepare and release cut (#755).
    • Enforce Prune=false,Delete=false sync options across all namespaces and persistent volume claims (#755).
  • Instruction surfaces and agent documentation:
    • Remove hardcoded gate counts across documentation surfaces, aligning with unified gated quality gate terminology (#840).
    • Rescope automatic Copilot review statements to PRs into main only, noting monthly token budget rationale (#840).
    • Update issue template references in AGENTS.md to supported templates (bug_report.yml, feature_request.yml) (#840).
    • Add --force flag to devops ai agents, refusing to overwrite existing AGENTS.md without --force while writing pointer stubs and exiting non-zero (#840).
    • Remove vestigial _pointer_stub helper from devops_cli.commands.ai (#840).
    • Add offline test suite tests/test_agents_md_claims.py verifying issue template existence, persona enum consistency, and pointer stub byte equality (#840).
  • Required CI checks bind merges into release and main:
    • Remove deprecated .github/branch-protection.yml and clarify help text across devops gh branch-protection commands to indicate management of classic branch protection rather than rulesets (#842).
    • Author ADR 0004 recording the CI verification policy, required check names (Static Analysis and Tests & Coverage), strict mode, update-prs.yml dispatch route, and owner decisions on Copilot reviews (#842).
    • Add glossary definitions for Gate, Check, and Required check to CONTEXT.md (#842).
  • Dependency updates from closed Dependabot pull requests:
    • Bump fastapi from 0.141.1 to 0.142.2 (#1201).
    • Bump gitpython from...
Read more

v0.2.26

Choose a tag to compare

@github-actions github-actions released this 04 Oct 19:56
e70df90

Added

  • Coverage Reverse Index Test Selection (devops ci coverage --build-index, devops ci test):
    • Added --build-index option to devops ci coverage to generate a per-worktree reverse coverage index (coverage_index.json) mapping source files to covering test files using COVERAGE_CORE=ctrace and --cov-context=test (#406).
    • Enhanced devops ci test to select tests deterministically from the reverse coverage index, automatically incorporating working-copy drift since index creation and falling back to text-based selection on missing sources (#406).
    • Added atomic persistence and pre-/post-run working-tree mutation verification during index generation, aborting and cleaning up when repository files change during the run (#406).
    • Enforced full test suite escalation when trigger files (conftest.py, pyproject.toml, uv.lock, .python-version) change, with --no-fallback rejection support (#406).
  • Ruff Correctness Rule Families (B, RUF, ASYNC):
    • Extended Ruff lint selection to whole rule families B (flake8-bugbear), RUF (Ruff-specific rules), and ASYNC (flake8-async), catching runtime bugs, mutable defaults, and unhandled exception chains (#420).
    • Selected targeted single rules: SIM115 (unclosed file handles), PT011, PT015, and PT017 (pytest assertion and raises integrity), and PGH003 (explicit type ignore codes) (#420).
    • Configured allowed-confusables for typographic mathematical and informational glyphs (–, ×, ℹ) and enabled ignore-without-code in mypy configuration (#420).
    • Resolved all 146 rule violations across the codebase, added architectural invariant tests for suppression rules and mypy configuration, and lowered the C901 suppression ceiling from 66 to 65 (#420).
  • devops-cli in the cluster (k8s/devops/, devops k8s run-job):
    • Namespace devops with a suspended CronJob template, a token-less service account and a default-deny perimeter; cluster jobs reach the gateway directly (#741).
    • devops k8s run-job -- ARGS creates a Job from the template with only its args changed, follows its log and exits with its exit code; --dry-run reads nothing, not even the CronJob, and prints the kubectl requests a run would make (#741).
  • Cluster Secrets from the keyring (devops k8s push-secrets):
    • One table lists every Secret the stacks read; the command adopts live values, generates the ones nobody types and needs --rotate to replace a live value (#741).
    • Server-side apply from stdin: values never reach argv, files, annotations or output (#741).
    • --dry-run makes no request, reads included, and prints the requests a push would make in order with placeholders; --plan reads the keyring, the cluster and, for the machine account, gh's own record of it, says so, and prints each key's state (#741).
    • The machine account's token comes from gh's keyring through gh auth alone: gh must list the login with a keyring token its own check passes, and devops-cli never calls GitHub with that token, so a run keeps one GitHub identity (#741).
  • Hidden prompt for credentials (devops config set KEY):
    • Omitting VALUE for a credential prompts for it, keeping it out of shell history (#741).
  • Single-Entry Item Intake (devops roadmap intake, FastMCP roadmap_intake):
    • Open issues off the board, unfinished items and new --title/--body-file candidates get a duplicate check, a code-checked type, Priority, Value and Effort, and a placement; P0 needs verified evidence from a trusted source (#742).
    • Agent candidates are labelled source/agent and open, borrow (budget/borrowed) or fold under the agent filing quota (#1153), with its numbers on every run (#742).
    • The dry-run rule (#412): --dry-run makes no request, to GitHub, a model or the telemetry collector, and prints the requests a run makes in order with placeholders, as an IntakePlan marked as a dry run, each request a PlannedRequest (#741) that shows the exact gh command where it is known without a read, the condition it runs on and whether it repeats per page or per candidate; --plan, which intake without a mode flag runs, reads GitHub and calls the model, writes nothing and reports its spend (GitHub requests by REST, REST search and GraphQL, embedding and proposal calls); --confirm writes. The FastMCP tool takes mode (plan by default, dry-run or confirm), and a FastMCP tool dispatched in-process with --dry-run exports no telemetry either (#742).
    • Agent instructions file new work through intake, and devops gh issues triage reports issues off the board as awaiting intake instead of missing a milestone (#742).
  • devops roadmap close: closes as completed each open issue that a pull request merged into the current release's branch closes, with one comment saying what changed (the pull request, its merge commit, the first section of its body and its changed-file count) and how it was verified (each check run with its bucket, and the Acceptance Criteria of the issue's task file at the merge commit). A pull request whose check runs can't be read closes nothing, and the run exits non-zero. Once the release holds no open item, at least one item closed as completed and no release pull request, it writes docs/ROADMAP.md on chore/cut-vX.Y.Z in the clone at --root and opens the release pull request into the default branch, ready for review, through the shared cut path; completed items with no changelog fragment are listed. Writes only with --confirm; --plan previews, --dry-run makes no request. MCP mirror roadmap_close previews only (#743).
  • The roadmap store reads every pull request merged into a branch, with its merge and head commits and changed paths, from both adapters (#743).
  • devops roadmap refine: item refinement to Ready with proposed design, tasks, and acceptance criteria. Selects New items from the next planned release by priority, then New backlog items up to the release cap. Gathers minimal repository context (CONTEXT.md, ADRs, cited files, grep identifiers) and up to 3 Tavily search queries for public repositories. Uses structured model output (RefinementProposal) validated deterministically against repository commits, cited lines, and verified sources. Writes sanitized plain-text markdown between markers <!-- devops-roadmap-refine:start --> and <!-- devops-roadmap-refine:end --> on the issue body with SHA/branch tracking and 65k size limit guard. Items that fit in one pull request with complete criteria and no open questions move to Ready with reason comments; oversized items remain New with refine.needs_split="true" and are descoped by reprioritization. Supports --dry-run and --confirm. Hooks into apply_intake for critical fixes and P0 features. Added FastMCP mirror tool roadmap_refine and due table integration in devops roadmap run (#744).
  • Continuous Service Mode for devops serve:
    • Added dedicated continuous service mode (devops serve --service) for in-cluster deployment, processing and authenticating GitHub webhooks with per-repo single-concurrency job dispatch and coalesced batches (#752).
    • Added ServiceConfig to configuration settings (repos, machine_account, poll_interval_seconds, drain_timeout_seconds) and managed credential service.webhook_secrets (DEVOPS_CLI_SERVICE_WEBHOOK_SECRETS) for repo-to-secret HMAC mapping (#752).
    • Added structured single-line JSON log formatter (JsonLogFormatter) emitting timestamp, level, logger, message, and contextual metadata with zero credential, payload, or signature leakage (#752).
    • Implemented POST /webhooks/github route with 25 MiB size enforcement, Content-Type verification, constant-time HMAC-SHA256 signature verification, 10,000-delivery LRU deduplication, ping handling, and machine-account suppression (#752).
    • Added /healthz, /readyz, and /metrics Prometheus endpoints recording delivery outcomes, trigger sources, job counts, job durations, start timestamps, and queue depth (#752).
  • Production Service Image for Scheduled Jobs and Webhook Handlers (Dockerfile, .github/workflows/ci.yml, .github/workflows/release.yml):
    • Root Dockerfile multi-stage build packaging Python 3.14 runtime, gh CLI, git, and devops-cli virtual environment into a hardened, non-root (1000:1000), read-only container with /etc/gitconfig credentials (#753).
    • Pinned base images (python:3.14-slim-trixie, ghcr.io/astral-sh/uv) by SHA256 digest with BuildKit layer caching and minimal attack surface (#753).
    • CI workflow service-image job validating content change detection, build/load smoke testing under full sandbox constraints (--read-only, --tmpfs, --cap-drop ALL, no-new-privileges), and Trivy vulnerability auditing (#753).
    • Release workflow service-image job publishing ghcr.io/dan-petty/devops-cli/service:v<version> and :latest with SPDX SBOM generation and cryptographic build provenance attestation via GitHub Actions (#753).
    • Dependabot docker ecosystem tracking in .github/dependabot.yml and release routine verification instructions in docs/ROUTINE_TASKS.md (#753).
  • devops roadmap run: evaluates the roadmap due table across landed jobs (intake, close, reprioritize) in a fixed execution order, tracking each job's last successful run time per repository in <data dir>/roadmap/<owner>/<name>/schedule.json. Clones repositories for jobs requiring code checkouts to <data dir>/roadmap/<owner>/<name>/clone, synced to the tip of origin/release/vX.Y.Z with session identity. Supports --dry-run and --confirm modes. Wires service_job adapter to devops serve --service for processing coalesced trigger batches. Adds FastMCP mirror tool roadmap_run reporting due jobs (#981).
  • Roadmap Service Kubernetes Deployment:
    • Added single-replica Recreate Deployment for roadmap-service in the devops namespace running `serv...
Read more