Repository navigation
Releases: dan-petty/devops-cli
Releases · dan-petty/devops-cli
Release list
v0.2.35
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, andTODO. - Added
devops ai research annotatecommand for recording and viewing marginalia with JSON and YAML formatting support. - Automatically injected prior marginalia into
devops ai research foragetrails and node context.
- Added file-anchored observation and hypothesis tracking under
- 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, andUNDERSPECIFIED_CONTRACTwith associated rationales. - Added minimal diagnostic probes emitting concise clarifying questions and concrete code lookup instructions bounded by
--max-probes. - Added
devops ai research socraticsubcommand 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
CrossDomainAnalogyandAnalogyReportstructured output models surfacing source module, target module, shared structural pattern, confidence score, and invariant mechanisms. - Added
devops ai research analogiesCLI 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 modelcommand supporting progressive revision updates,--observations,--file,--show, and YAML, JSON, Markdown, and table formatting.
- Added progressive synthesis of sprawling research observations into structured YAML mental models under
- Kubernetes orphaned volume reclamation (
devops k8s volume reclaim):- Added
devops k8s volume reclaim <name>to safely reclaim persistent volumes stuck inReleasedorFailedphase. - Implemented pre-mutation verification confirming volumes are genuinely orphaned: phase check (
ReleasedorFailed), claim absence check, and pod mount check across all namespaces. - Added confirmation gating requiring
--yesin non-interactive sessions, and request-free--dry-runsimulation displaying exact API operations.
- Added
- Standardized output format adoption (
--format,-fsupportingtable,json,yaml,csv) across all 144 CLI value commands viaemit_serializedandresolve_format(#411). - Structured property-based fuzzing test suites for boundary resilience across SARIF 2.1.0 parsing,
git worktree list --porcelain,git status --porcelain=v1cache parsing, Argo CD drift analysis, and review JSON schema normalization (#416). - OCI container image packaging & standardized metadata validation (
devops devcontainer validate):- Added
--oci-labelsand--expected-sourceoptions todevops devcontainer validateto 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.
- Added
devops ai gateway render: render and apply LLM gateway configuration derived from Ollama profiles and service aliases, aligningmax_input_tokenswith tier context lengths, removing deprecatedweightfields underleast-busyrouting, 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/tempo3.1.0) in theotelnamespace backed by a10Gilocal-pathPersistentVolumeClaim (Prune=false,Delete=false) with168h(7-day) trace retention, Argo CD Applicationk8s/argocd/apps/tempo.yaml, Alloyintegrations.tempometrics scraping, and GrafanaTempodatasource (uid: tempo) linked toloki(tracesToLogsV2,lokiSearch) andpyroscope(tracesToProfiles) (#1135). - Add
TempoConfig(settings.tempo),devops k8s tempo, MCP toolk8s_tempo_info, built-in AI toolk8s_tempo_status, and Tempo OTLP v2 trace normalization (normalize_tempo_spans,query_tempo_trace) fordevops telemetry profileanddevops sandbox traces(#1135).
Changed
- Container manifests & release workflows:
- Standardized OCI Image Spec labels across
Dockerfile,.devcontainer/Dockerfile, and.github/workflows/release.yml. - Pinned
uvto0.12.23across devcontainer and production Dockerfile builds.
- Standardized OCI Image Spec labels across
- Route OpenTelemetry Collector trace exports to
otlp_grpc/tempo(tempo.otel.svc.cluster.local:4317) and updatek8s/monitoring/dashboards/otel-collector.jsonto queryexporter="otlp_grpc/tempo"and thetempoGrafana datasource (#1135).
Removed
- Remove the ephemeral
jaegerDeployment and Service (k8s/otel/jaeger.yaml),settings.jaeger,devops k8s jaeger,k8s_jaeger_info, andk8s_jaeger_status; on clusters not managed by Argo CD, remove the old resources withkubectl 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.jsonandreview.mdconsistently 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_TOTALrather than falling back all findings to the default persona.
- Ensured PR review comments and findings telemetry metrics are built from verified findings even when the consolidated reporting stage is skipped via
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
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
heapqand cycle-detection ledger, scoring edges via BM25 lexical similarity (ai/lexical.py) and backtracking when information scent drops below threshold. - Registered
devops ai research forageCLI command with interactive summary panels, detailed trail tables, and structured--jsonoutput.
- 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 excludingdocs/ROADMAP.md. - Added reusable defect PR classifier (
classify_defect_pr,is_defect_pr) insrc/devops_cli/github/defect_classifier.pyidentifying defect pull requests bytype/bug,type/securitylabels or Conventional Commitsfix(...)/fix:titles. - Added post-merge audit workflow
.github/workflows/defect-pr-hardening-audit.ymltriggering 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.pyvalidating triggers, permissions, step commands, intake routing, and static Typer command resolution.
- Implemented
- Worktree inventory & safe orphan reclamation (
devops repos worktrees):- Added
devops repos worktrees statuscommand 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'slockedandprunablestatus. - Added
--jsonoutput option emitting typed arrays containingahead(int),dirty(int),contained(bool),locked(bool), andprunable(bool). - Added
devops repos worktrees prunewith safe dry-run preview by default; without--force, removes only clean and contained worktrees and refuses unmerged, dirty, or locked checkouts. - Added
--forcesupport to remove worktrees regardless of clean or unmerged status, cleaning up metadata withgit worktree prune. - Implemented NUL-separated porcelain parsing via
git worktree list --porcelain -zper the #1440 convention to safely support worktree paths with newlines.
- Added
- AGENTS.md Context Budget Enforced (
src/devops_cli/config/constants.py):- Declared
CONST_AGENTS_MD_MAX_CHARS: Final[int] = 40_000defining the maximum character budget forAGENTS.md. - Added unit test in
tests/test_agents_md_claims.pyverifying thatAGENTS.mdlength is withinCONST_AGENTS_MD_MAX_CHARSandCONST_AGENTS_MD_MAX_CHARS <= 40_000. - Added
@AGENTS.mdimport directive generation togenerate_pointer_stubinsrc/devops_cli/ai/instruction_generator.pyfor Claude Code memory loading.
- Declared
- 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.sockand/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_CONNECTIONis detected, guiding configuration toCONTAINER_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) configuringuser="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 cachewith informative guidance directing users todevops docker prune. - Propagated resolved
DOCKER_HOSTto Dive container analysis and grantedXDG_RUNTIME_DIRpass-through to Cosign image verification.
- Implemented automatic container engine discovery across
Changed
- Poetically Concise AGENTS.md:
- Condensed
AGENTS.mdfrom 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.mdto load@AGENTS.mdusing Claude Code memory import syntax.
- Condensed
- The roadmap service is suspended (
k8s/devops/roadmap-service): GitHub suspended the Service's machine account, sodeployment/roadmap-serviceasks for 0 replicas, and an Argo CD sync ordevops k8s deploy-stack --stack devopsno longer starts it. AGENTS.md andk8s/README.mdopen their roadmap-service sections with the suspension: nobody scales, restarts or re-pushes secrets for the Service, only a person runs mutatingdevops roadmapcommands, and agents leave the Service's items labelledstatus/blockedalone.
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) inconfig/defaults.pyandconfig/constants.pywith no consumers insrc/ortests/.
- Removed dead constants and defaults (
- Removed vLLM deployments, configuration, and leftover policies:
- Removed dead vLLM cluster URL defaults (
DEFAULT_VLLM_16GIB_CLUSTER_URL,DEFAULT_VLLM_48GIB_CLUSTER_URL), settings fieldAIConfig.vllm_url, router methodGatewayRouter.scale_vllm, CLI commanddevops ai gateway scale, and FastMCP toolai_vllm_scale. - Removed vestigial vLLM Kubernetes manifests, perimeter policies, and port 8000 egress rules across
k8s/llm/andk8s/monitoring/, includingk8s/llm/profiles/networkpolicy.yaml(vllm-profiles-perimeter) and Portkeyvllm-backend. - Removed vLLM server busy-share and waiting queue metrics from
devops ai gateway loadand pool load metrics calculation. - Updated
.devcontainer/devcontainer.jsonOllama endpoint tohttp://ollama-16gib.llm.svc.cluster.local:11434.
- Removed dead vLLM cluster URL defaults (
- Legacy segment review engine & whole-file reviewer:
- Deleted legacy segment review engine and whole-file reviewer in
ai/review/runner.py, leavingReviewPipelineOrchestratoras the sole review pipeline. - Deleted dead
_run_persona_loop, segment chunking, recompose functions, and_save_findings_jsonfromai/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.composeconfiguration field,AI_TASK_COMPOSE_*settings,DEVOPS_CLI_AI_TASK_COMPOSE_*environment variables,ReviewClients.compose, andPersonaDefinition.compose_prompt.
- Deleted legacy segment review engine and whole-file reviewer in
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-pricesdataset instead of replacing entire catalog. - Preserved token extraction for both unbundled and partial bundled providers after snapshot ingestion.
- Recorded installed
genai_prices_versionand UTCfetched_atin snapshot metadata, falling back to bundled data with warning if versions mismatch. - Preserved recorded fetch timestamp as
auto_update_timestamponDataSnapshot. - Added
--resetoption todevops ai cost update-pricingto clear snapshot and caches, with mutual exclusion against--source. - Added
--format yamlsupport, base-tier rate column headers, and table captions todevops ai cost list-pricing. - Calculated tiered request costs dynamically in
SpendGuard.record_usageandEmbeddingResult.costfallback, while honoring flat-rate caller pricing.
- Merged remote and custom partial pricing snapshots field-by-field onto bundled
- Cloudflare tunnel route synchronization and Kubernetes URL setup preservation:
- Preserved per-route
originRequestconfiguration (noTLSVerify,httpHostHeader), custom catch-all ingress rules, andwarp-routingsettings duringdevops 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-runperforms zero network requests while outputting serialized GET and PUT request payloads. - Matched AI gateway ingress backends via
CONST_K8S_INGRESS_SERVICE_PATTERNSusing exact service-name and namespace matching rather than substring searches. - Protected pre-configured remote custom
ai.gateway_urlendpoints 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.
- Preserved per-route
- Review early exits & zero-model routing (
devops review):- Exited
--summarybefore `ReviewPipelineOrches...
- Exited
v0.2.33
Added
- Commands with a dry-run mode in the
ai,argo,devcontainer,docker,docs,grafana,k8s,prometheus,release,repos,review,ssh,telemetry,tf,tlsandvaultgroups now declare--dry-run, among themdevops tf apply,devops k8s configure-urls,devops release taganddevops vault logout, sodevops <command> --dry-runruns that mode where it used to fail withNo such option: --dry-run(#412). Most of these commands print their preview and stop;devops ai analyze path,branchandprstill analyse, without the model, and print the metadata they would save, anddevops argo rollouts analyzestill evaluates its metric gate but does not abort the rollout. devops release sync-notes --dry-runprints the repository and the releases it would republish, then stops without reading any release from GitHub. With--allit names<all published releases>instead of listing them (#412).- CI's static job uploads the SARIF of
devops scan reportto code scanning under category/devops-scanon every push and pull request, so each scanner's findings show on pull requests and close once fixed (#419). devops review path,devops review branchanddevops review prrun mypy over the reviewed Python files and report its errors as findings (#873). mypy runs on the main worktree's.venvPython, or on devops-cli's own interpreter when the main worktree has none, with the reviewed tree'spyproject.toml, or else the main worktree's, as its config file. It runs only when the reviewed tree and the main worktree hold the sameuv.lock, because a review never runsuv syncon a reviewed tree, so a project without auv.lockgets 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 reachesreview.mdorfindings.jsonyet: Ruff names each file by afile://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.tomltakes[advisory] rules, the Ruff rules a review adds to the project's own selection, and defaults toBLE001(#873). Like Ruff's other findings, theirs do not reachreview.mdyet.review.mdgains a## Complexitytable 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'sfindings.jsonrecords the same rows ascomplexity_delta. A path review has no base, so it lists every function as added. A changed or added function that ends above[complexity] max_complexityin.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.pyfails when a file undersrc/holds more swallowed failures than its ceiling intests/fixtures/swallowed_failures_ceilings.json, where a file not listed has a ceiling of 0 (#1347). It counts Ruff'sBLE001,S110andS112findings, and each call passingcheck=Falsethat stands alone as a statement or is assigned to a name never returned, yielded or read for.returncode,.stdoutor.stderr. The failure lists every site in each file over its ceiling asfile:line: ruleand 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 recountssrc/, 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--confirmit 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--commentwhen 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--confirmit prints the item's number and title, the target status and the comment, and writes nothing.--dry-runmakes no request and prints the requests a run would make, and--dry-runwith--confirmis refused.AGENTS.mdasks 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 withdevops 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.tomltakesrelease_slots, the items a release holds aboverelease_capbefore 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 withDEVOPS_CLI_DRY_RUNexported, now runs the command with--dry-runwhen the command it resolves to declares that option, instead of printing the genericWould 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-runstill prints only the generic line and does not run,-hor--helpshows the command's help, anddevops --helpsays that a delegated command previews only if it declares--dry-run.devops --dry-run <command>, or a run withDEVOPS_CLI_DRY_RUNexported, 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-rungiven an option or argument it rejects, or missing a required one, such asdevops --dry-run release preparewithout a version.docs/SDLC.mdrequires state-mutating commands to declare--dry-runinstead of using@dry_run_command(#412).devops review verifyrecords 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.tomlwith 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-readinesslist the unexpired.devops/review.tomlsuppressions 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-reviewgives a tools-only review until the persona stage is removed (#1151).devops ai prompt-evalcounts the verdicts the feedback dataset records per labeller, since the deterministic layer it replayed is gone (#1150). - When
devops ai agentshas a model writeAGENTS.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--templateor when no model answers, adds the feasibility check, the hand-back withdevops roadmap returnand the four stop signals only to theAGENTS.mdit writes for devops-cli itself. - The task-file template in
docs/agent/tasks/README.mdhas a**Feasibility**line, andtests/test_agent_task_files.pyfails any task file without one (#1348). Task files that predate the rule are exempt and are listed intests/fixtures/task_files_baseline.txt. - A release cut with no changelog fragment and no entries under its version's
CHANGELOG.mdsection fails before it pushes, naming the version, its release branch, the missing section and, fromdevops roadmap closeor the roadmap Service, the completed items with no fragment (#1450). devops pr check-readinesslets a pull request into a cut release branch editCHANGELOG.mdonce 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 changesCHANGELOG.mdbefore the cut is still blocked,docs/ROADMAP.mdstays the cut's, and a failed read of the branch'sCHANGELOG.mdis a blocker naming the error.devops roadmap close --planlistsCHANGELOG.mdamong the cut's files and the fragments the cut collects (#1450).devops roadmap reprioritizestill fills a starting release torelease_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. Thecap,over_sizeandtrimreason comments n...
v0.2.32
Added
- Pull requests show changed-line coverage against their base branch (diff-cover), plus any added
pragma: no coverlines, 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.
addoptsblocks it with-p no:randomly, sopytest,devops ciand every other run keep file order, anduv run pytest --co -qcollects the same order as before (#852). devops ci test --repeat N --seed Sruns 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.--repeatrefuses 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.ymlrunsdevops ci test --repeat 3over 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--changedit maps the functions the working tree changed since its merge base with--base(defaultmain), untracked files git does not ignore included, to mutmut's names, and a--basenaming 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 adevops cistage nor aci.ymlstep. The first run mutates the whole source tree into the git-ignoredmutants/and runs the test suite once, serially; later runs reuse it, andrm -rf mutantsresets it.- An architectural invariant fails on any
# pragma: no mutateundersrc, so no line exempts itself from mutation (#853).
Changed
- The settings parser refuses scheme-less URLs for
argocd.url,grafana.url,ai.ollama_urls, andOLLAMA_BASE_URLwith a prescriptiveConfigurationErrorinstead of prefixinghttp://. - 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
--providerto benchmark external endpoints. - Sandbox whitelist parsing refuses entries containing empty port suffixes (
example.com:,example.com:/path). - Failed repository clones wrap
gitlib.GitCommandErrorintoGitOperationErrorand 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 tomain,release.ymlbuilds the image, scans it, pushes it by digest and attests its provenance, then tags itvX.Y.Zandlatest, and Image Updater moves Applicationdevopsonto the new digest within one poll, with no git commit.
Removed
- In-process
known_hostsparser and runtimessh-keyscaninvocation, delegating host key validation to OpenSSH. - The release cut's bump of the Service image's
newTagand its rewrite of the Argo CD git sources'targetRevision(#1486). devops release check's checks of the Service image pin and of the Argo CDtargetRevisions (#1486).- The
images:pin ink8s/devops/kustomization.yaml(#1486). - Service image publishing from
release/vX.Y.Zinrelease.yml, with its publication plan and the step that pointedlatestat an image the release branch published (#1486).
Fixed
- The
devops reviewcommand 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 aMagicMocksettings 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
/tmpon an emptyDir (#1060).HOMEis/home/ollama, so the key and models stay on the hostPath/var/lib/ollamaand nothing is downloaded again. A root initContainer,own-model-directory, holdingCAP_CHOWNalone, gives that directory to UID 10001 at every start, becausefsGroupdoes not apply to hostPath volumes; it is the onerun-as-non-rootexception kube-linter reports, recorded in the manifest andk8s/README.md.test_every_ollama_tier_runs_unprivilegedpins 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-urlshandles 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 viaappend_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.URLandread_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-singletoncollector runs as one Deployment pod through the chart'ssingletonpreset 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 duplicatek8s_cluster_namelabel (#1129). kube_hpa_labelsis 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 ofubuntu-latest, and uv inci.ymlandrelease.ymluses only its own CPython builds (UV_MANAGED_PYTHON). GitHub movesubuntu-latestto 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-versionpins. The move toubuntu-26.04waits 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.lockpins fordevops install-tools, instead of 0.70.0, and a workflow contract test fails whenever anaquasecurity/trivy-actionstep'sversiondiffers from the lock (#1499).
Security
- The host sandbox no longer declares a noexec
/tmpthat 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 theirsize=64m,noexecpolicy. A host sandbox whose policy declares a tmpfs option bubblewrap cannot apply, such asnoexecorsize=50%, now fails at construction with an error naming the option, instead of dropping it. SECURITY.md now says that the host sandbox's/tmpallows 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
Added
deptryquality gate step enforcing thatpyproject.tomldeclares all directly imported dependencies and flags unused or transitive packages (#1120).- Argo CD Image Updater keeps the
devopsApplication on the digestghcr.io/dan-petty/devops-cli/service:latestpoints at. Applicationargocd-image-updaterinstalls the argo-helm chart pinned to 1.3.1 in namespaceargocd, and its one ImageUpdater (k8s/argocd/image-updater-values.yaml) writesservice:latest@sha256:<digest>into the Application'sspec.source.kustomize.imageswithin one 2-minute poll oflatestmoving, with no git write-back. roadmap-service and CronJobdevops-cliroll onto a release oncerelease.ymlpointslatestat it, and thenewTagpin ink8s/devops/kustomization.yamlno longer decides the running image.k8s/README.mdshows 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) acrossdevops_cli.aiagents, models, and provider packages until first use (#1338).
Removed
Timeoutre-export removed fromdevops_cli.ai.settingsanddevops_cli.ai.agents;create_model_settingsandresolve_runtime_model_settingsaccept numeric seconds asfloat | 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 underrepos/on port 9418 (#1122). - Dropped
DEVOPS_GIT_DAEMON_AUTOSTARTandDEVOPS_GIT_DAEMON_PATHSconfiguration environment variables (#1122). - Dropped port 9418 forwarding and attributes from
.devcontainer/devcontainer.jsonand the devcontainer template, and removed port 9418 egress from Argo CD network policy (#1122).
- Removed the background Git daemon service stub and its step from
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 throughresolve_store_pathunder the repository data directory, respectingDEVOPS_CLI_DATA_DIRanddata.dirsettings instead of resolving relative to the working directory. An architectural invariant prevents direct.datapath construction acrosssrc/(#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 underTERM=dumb, eliminating width-dependent test discrepancies between local runs and GitHub Actions CI (#1041). devops docker pushnow 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
SSRFBlockedErrordetails 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:9beforea.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 throughDEVOPS_CLI_USER_DATA_ROOTacrossgit/worktree.py,core/repo.py, andtools_lock, and worktree tests assert paths stay undertmp_path(#1311). - The Ollama round-robin index lives under
DEVOPS_CLI_DATA_DIRinstead of the system temp directory, andtempfile.gettempdir()resolves undertmp_pathfor every test (#1311). - The floor's patches of
CONFIG_PATHandCONST_USER_DATA_ROOTintests/conftest.pyare 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.yamlrule 4 now admits Traefik on TCP 8888, allowing theotel-metrics-ingressroute to reach the OpenTelemetry collector through Traefik (#1371).devops k8s deploy-stacknow deploys bothargocd/networkpolicy.yamlandotel/networkpolicy.yamlnatively under theinfrastack 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-runanddevops test sandbox --dry-runnow validate the workspace directory viaWorkloadSandboxRunner.build_dry_run_details(), refusing forbidden roots, home directories, socket directories, and sensitive paths withDockerSandboxErrorbefore 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_KEYto"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).
- Pinned finding fingerprints (
- The Docker daemon endpoint check now covers the exact endpoint the SDK dials, resolving endpoints in priority order across
DOCKER_HOST,DOCKER_CONTEXT,currentContextinconfig.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, requiringai.allow_private_networkfor 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.parseandhttpx2), 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 originalhttp://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-ipplaceholder (#1109). - Strip userinfo credentials from gateway
served_byheaders (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 scanandNormalizedFindingto 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@versionfor dependency advisories) (#1141). - Dropped the
0.0.0.0/0ingress peer fromk8s/argocd/networkpolicy.yamlandk8s/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 anipBlockpeer with cloud metadata SSRF protection, allowing GitOps reconciliation against the Kubernetes API server (#1371).
v0.2.30
Added
devops sandbox network-policy --allow-collectorandsandbox_network_policy(allow_collector=True)add one egress rule to the OTel collector's pods: theotelnamespace and theopentelemetry-collector/otel-collectorpod labels together, on TCP 4317 and 4318, with noipBlock. It applies to thesandbox_namespace,public_whitelistandlocal_whitelistmodes; withisolatedorbridgethe command exits 1 naming those modes (#702).- Sandbox containers and execs carry trace context: with telemetry enabled,
devops sandbox deploy,devops docker sandboxanddevops test sandboxgive the containerTRACEPARENT(andTRACESTATEwhen set) parented to thesandbox.deployordocker.workload_sandbox.runspan, and eachdevops sandbox execruns in its ownsandbox.execspan and passes that span's context to the command.DockerEngineService.exec_in_containertakesenvironment. 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 areview-scorerun, 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_v2of 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.jsonholds 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 DIRwrites 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 checktakes--max-precision-dropand--max-stability-dropfor review score runs, and--max-calls-increasefor 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.mditem 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 tooldevops install-toolsinstalls, 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 filesuv pip compile --generate-hasheswrites (#1142).devops install-toolsinstalls gitleaks, osv-scanner, Semgrep and Bandit. A Python tool installs withuv pip sync --require-hashesinto a relocatable virtual environment of its own. uv runs with--no-config, so auv.tomlor[tool.uv]in the current directory cannot change the install, and keepsUV_MALWARE_CHECK,UV_CACHE_DIRandUV_LINK_MODEfrom the environment (#1142).devops install-tools --checkinstalls nothing: it lists each tool whose command is not linked at its locked version and exits 1 if there is one (#1142).profile.jsonrecordstools_lock_digest, the digest of the tools lock a review started with, andcode_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 orhttps, and 80 forhttp. 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 as140.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, MCPscan_complexityand the inspection hotspots report standard McCabe complexity, as Ruff'sC901counts 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 insrc/thatruff checkdoes, where it flagged 244 (#773).- The
structural-invariantspre-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: anexcept,match,assert,async fororasync withblock now opens a level, and atrybody no longer does.
- It runs from
devops ai runs checkalso 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 withdevops review scorein place of an inlinescore_recall.pyscript. 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-toolsexits 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. Itssemgrepandpysemgrepboth link to the locked environment, becausesemgrepruns thepysemgrepit finds on PATH, and they replace the linksuv tool installleft (#1142). devops install-tools --only-missinginstalls each tool whose command is not linked at its locked version; it used to skip any tool found on PATH (#1142).devops install-tools statusmakes 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, andanalyze_file_complexityandFileComplexityReportfromdevops_cli.security.complexity;devops_cli.core.code_metricsreplaces them (#773).devops install-toolsno longer looks up any tool's latest release, and its--versionoption is gone: the lock is the only version source.validate_version_strand the per-tool download URL constants go with it (#1142).
Fixed
devops branches jiranow creates and checks out the feature branch; it always failed ongit 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
CheckandRemediationbesideDiagnostic, but the adapter read the check fromDiagnostic, 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 SARIFruleId; its fix is kube-linter's remediation; it is located at<manifest>:<Kind>/<namespace>/<name>, or<Kind>/<name>when the manifest names no namespace (nodefaultis guessed); and its category is the class the check or the message names, elsesecurity_misconfiguration.devops k8s lintprints 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 reportkeeps every finding and lists findings on different objects as separate rows: ...
v0.2.29
Added
devops vault logout: revokes the tokendevops vault loginstored, 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 loginstored as a bare string is no longer used; rundevops vault loginagain (#709). devops gh secrets sync --source vault --dry-runno longer reads Vault: it lists each secret it would read, seal and upload, so the dry run sends no request (#709).devops gh project syncanddevops gh project reconcileadd no card: an issue reaches the board throughdevops 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 --planreads 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. MCPgh_project_reconciletakesmode(dry-run,planby default, orwrite) in place ofdry_run, so a call with no arguments no longer writes (#892).devops gh project syncreports 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 projectanddevops gh views--templateoption defaults to.github/project-template.jsonat the repository root, so the commands work from a subdirectory;reconcile --project-numberreads the board's own Status options and loads no template (#1010, #892). DEVOPS_CLI_AI_ALLOW_PRIVATE_NETWORKis read only throughai.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 withallow_private=Falsestays public-only, and anAIConfigbuilt in code (an agent given a model name,model_request_sync, the chaos fallback,GatewayRouter()with no config) takes its ownallow_private_networkrather than the variable (#898).new_http_clientandnew_async_http_clienttake a requiredleveland ownverify,http2andlimits; tenacity retries are opt-in withretries=, because the retry transport turns every 4xx and 5xx into an error.HttpClientBrokerkeeps one client per level, andrequest,arequest,new_client,get_clientandget_async_clienttake the level; every Vault request (vault_request) uses the private level.get_shared_clientandget_shared_async_clienttake 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_networkis 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).
httpcore2andanyioare declared dependencies, at the versions already locked, andh11, 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_intakeandintake_candidatetake the repository they run on (repo), whichdevops roadmap intakeand the Service's intake job pass (#1358).- The intake
--dry-runrequest plan lists the.github/labels.ymlread first among the candidate reads, before the timeline reads and the embedding call, with the condition that the run has a candidate; for a--titlecandidate it has no condition (#1358). devops roadmap intake --limit Ndecides the N oldest candidates and lists those it left; without it intake decides every candidate, as before, and its--dry-runstill makes no request (#1360).- The reprioritize row's webhook hints are
milestoneclosedandreleasepublished(CONST_ROADMAP_REPRIORITIZE_BATCH_KEYS); any other milestone event is no longer one (#1360). ChangeKind.RELEASE_STARTED, which nothing recorded, is removed, andIssueRecordcarries the issue'supdated_at(#1360).RoadmapStore.add_itemreturns the Item it added,ItemandBoardEntrycarry the card's node id (card_id), andchanges_sincetakesexcept_actor, an empty one leaving out nothing. The run record card is written by the idgh project item-create --format jsonreturns, with no board read after the create (#1361).- The
--dry-runrequest plans list each board write's card read and itsitem-editby 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_floorandrequire_floorput the budget's reset in their error's details (reset_at, ISO 8601), andrefusal_resetreads it back (#1400).- A roadmap store that has sent no GraphQL request reports no spend and sends no budget query (#1400).
- A
devops roadmapcommand whose store sent none ends with no GraphQL line. - The
--dry-runplans 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.
- A
LocalDurabilityCapabilityis built on pydantic-ai's public durable engine API: anengine_specand 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. Anevent_stream_handlermust be async, as pydantic-ai'sEventStreamHandlertype requires. pydantic-ai now rejects a second durability capability on the same agent (#1446).- A
PydanticAgenttool that takes a context must annotate it with pydantic-ai'sRunContext. A tool annotated withdevops_cli.ai.agents.context.RunContextnow fails schema generation, because the private pydantic-ai hook that accepted the subclass no longer exists. Useisinstanceto narrow to the subclass when the tool readsloaded_capability_idsortool_call_approved(#1446). calculate_usage_costprices throughgenai_prices.calc_priceand returns itsPriceCalculation, or None when genai-prices cannot price the model or usage.get_active_traceparentreads the active span through OpenTelemetry's W3C trace-context propagator (#1446).
Removed
- The
devops_cli_llm_cache_hits,devops_cli_llm_cache_missesanddevops_cli_llm_cache_writesgauges (#816). Each sent 1.0 from a short-lived process and counted nothing;devops_cli_ai_requests_total'scachedlabel 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_FLOORis the one GraphQL threshold (#892).- The broker's per-request
allow_private_networkoverride, itsallow_private_networksargument andHttpClientBroker.validate_url;ALLOW_PRIVATE_NETWORK_ENVindevops_cli.ai.clientandCONST_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_limitsmoved fromdevops_cli.http.pooltodevops_cli.http.client(#898). devops_cli.ai,devops_cli.ai.agents,devops_cli.ai.agents.pydantic_agent,devops_cli.ai.resultanddevops_cli.ai.runno 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,SyncStreamBridgeandcurrent_otel_traceparent(#1446).
Fixed
- Vault commands use the token
devops vault loginstores: the broker resolves its token from the argument, then the stored login (only for the Vault address and namespace that issued it), thenVAULT_TOKEN, thenDEVOPS_CLI_VAULT_TOKEN, through the shared resolver, sodevops vault auditrecords each lookup.vault statusshows a Token row naming the source, or the address a stored token that was passed over was issued for, and the dry runs ofget,set,syncandleasesname the token source (#709). vault set,vault syncand everyvault leasesmode exit 1 before any request when no token is usable, namingdevops vault login,VAULT_TOKENandDEVOPS_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 refusedvault leases --revokeprints the status Vault answered (#709).vault getanswers 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 vaultand the shared secret chain read Vault alone, andvault syncof a path with no secret exits 1 instead of reporting zero secrets (#709).vault login --storeexits 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...
v0.2.28
Added
- Every reported scanner finding and dependency advisory is admitted with its producer's anchor (
ToolAnchororAdvisoryAnchor) and a location validated against the reviewed commit indevops_cli.review.admission.admit(), writingfindings.sarifand rendering introduced, pre-existing, and suppressed findings inreview.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 inprofile.json; an inline marker suppresses only findings of its own tool (nosecfor Bandit,nosemgreporno-semgrepfor Semgrep,noqafor Ruff), and findings from the review's models are not admitted (#871, #1295, #1341). - Increase in-cluster
roadmap-serviceanalysis task context window ink8s/devops/configmap.example.yamlfrom 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
NoMatchesworker failures (#519). - Isolate MCP in-process child process stdio to temporary files to prevent protocol stream corruption, return bounded masked failure output with FastMCP
ToolErroron non-zero exit codes, and expandci_runtool to accept any gate check name with budgeted timeout (#839). - Ensure
web_fetchreports 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, andk8s/llm/profiles/README.md) to name only commands, options, flags, and architecture patterns that exist, backed by static argv validation indevops 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 -Mto 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/headand mark partial context when unavailable (#1047). - Path classes loaded from
.devops/review.tomlroute fixtures and golden sets to secret scanning only, skipping general analyzers (#1047). - Detailed per-tool coverage matrix recorded in
profile.jsonand rendered under## Coverageinreview.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-contradictionnote rather than endingVERIFIEDorverifier-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, ande5families (#1065). - Pin
CONST_KNOWN_EMBEDDING_DIMENSIONS["qwen3-embedding"]to 1024, honor configuredai.rag.top_kandai.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
--forceresumes 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-busyrouting andcancel_on_disconnect: true, avoiding edge cut stalls and backend queue build-up on Ollama (#1069). devops ai gateway connectautomatically discovers and configures the gateway's NodePort address on the LAN, probing/health/livelinessand/model/infowhile 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 toai.ollama_max_parallelperai.ollama_urlsentry 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
mainacross all Argo CD git-sourcetargetRevisionfields underk8s/argocd/while retaining the release version pin for the service image ink8s/devops/kustomization.yaml(#1261). - Fix Portkey AI Gateway liveness and readiness probe path to
/and align GPU Feature Discovery image tonvcr.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: 3100forloki-ingress, deduplicate Grafana dashboard ConfigMap ownership betweenbaseandmonitoring, remove redundantService/pyroscopealias colliding with the Pyroscope Helm chart, and remove the homelab domain from the committed overlays; a nativedeploy-stack --stack infraappliesk8s/monitoring/dashboardsitself (#1279, #1297). - Supply the homelab's values to Argo CD at deploy time instead of from the repository (#1290):
devops k8s deploy-stackrenders ConfigMapdevops-cli-configfromconfig.yaml, outside any Application.- It writes the hosts each of the Applications
devopsandingressrenders, at the revision Argo CD builds (or--argocd-revision), onto the Application as Kustomize patches. Theclusterapp-of-apps ignores those patches. - It renders everything before it writes, and refuses an unset service setting or domain by name.
teardown-stackdeletes the ConfigMap, and the repository no longer tracksk8s/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
Added
- Kubernetes deployment health diagnosis (
devops k8s doctor):- Add
devops k8s doctorcommand andk8s_doctorFastMCP 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-runexecution with deterministic ranking and secret masking (#408).
- Add
- Argo CD GitOps cluster state reconciliation:
- Introduce a two-level root Application topology (
bootstraptrackingmainandclustertracking the active release branch) managing 20 leaf Applications (8 raw manifests and 12 multi-source Helm charts) (#755). - Establish
homelabandhomelab-systemAppProjects 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-adminsecret to keyring push table with live credential adoption (#755).
- Introduce a two-level root Application topology (
- Sequential pipeline stage context budgeting:
MultiAgentPipelineallocates carried outputs from prior stages newest-first up toDEFAULT_PIPELINE_STAGE_CONTEXT_TOKENS(4,096 tokens, overridable via--stage-context-tokensor constructor), listing older stages that get no budget by name only and appendingScratchpadBuffer.render_context_summary()once after carried outputs outside the budget (#859).PipelineStepResultrecordscontext_tokens(exact token count of carried context sent to the stage) andcontext_truncated(#859).
devops pr update --dispatch-ciflag:- Add
--dispatch-citodevops pr updateto poll the head branch SHA and triggerci.ymlvia GitHub Actions workflow dispatch after updating branches (#984). - Grant
actions: writepermission exclusively toupdate-pull-requestsin.github/workflows/update-prs.ymland add architectural invariants gating Actions write permissions across all workflows (#984).
- Add
- Pinned invariants survive memory summarization, lead every system prompt and recur on a cadence:
- Partition
AgentMemorywith aninvariantsstore that survives extractive and LLM summarization as well asclear()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 seeddevops ai chatsessions withDEFAULT_CHAT_INVARIANTS(#985).
- Partition
- OpenTelemetry tracing for background service and Kubernetes workloads:
- Enable OpenTelemetry export in
k8s/devops/configmap.yamltargeting the in-cluster collector athttp://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 namespaceotelfor all current and future workloads in namespacedevops(#1197). - Configure
OTEL_SERVICE_NAME: roadmap-serviceink8s/devops/roadmap-service/deployment.yaml(#1197). - Instrument
create_service_appwith HTTP request tracing and timing middleware (skipping trace spans on/healthz,/readyz,/metricsprobes) and trace batch job executions inRepoWorker._execute_batch(#1197).
- Enable OpenTelemetry export in
- DevOps Stack in
deploy-stack&stack all:- Add
"devops"stack todevops k8s deploy-stackand automatically include it indevops k8s deploy-stack --stack all, ensuring all in-cluster manifests (networkpolicy.yaml,serviceaccount.yaml,configmap.yaml,cronjob.yaml, androadmap-service) and secrets are deployed and kept up to date (#1225). - Pre-register namespace
devopsink8s/namespaces.yamlwithrestrictedpod-security standards and ArgoCD Prune=false annotations (#1225). - Add automated rollout restart of
deploy/roadmap-servicein namespacedevopsduring stack deployment to apply updated ConfigMap values immediately (#1225). - Update
AGENTS.mdinstructions with the architectural requirement that all in-cluster Kubernetes resources must have a managed stack path indeploy-stack --stack all(#1225).
- Add
- 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, anddevops_cli_project_forks_total) (#1227). - Implement
record_project_metrics_in_registryto populate in-memory metrics registry for direct Prometheus scraping viaroadmap-service:8000/metrics(#1227). - Add scheduled
metricsjob toDEFAULT_DUE_TABLEinroadmap-servicerunning 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 VelocityGrafana dashboard (k8s/monitoring/dashboards/project-metrics.json) (#1227). - Add traffic and referral summaries to
devops gh metricsCLI output (#1227).
- Add GitHub repository traffic and engagement telemetry instruments (
- Dynamic DevOps ConfigMap Generation & Sanitized Template:
- Add
k8s/devops/configmap.example.yamltemplate with sanitized placeholder repositories and account name (#1231). - Add
/k8s/devops/configmap.yamlto.gitignoreand untrack it from version control to prevent user repos and bot accounts from leaking (#1231). - Add
devops_cli.k8s.configmapmodule withensure_devops_configmapandrender_devops_configmap_contentto dynamically materialize and synchronize the cluster ConfigMap from active configuration (service.repos,service.machine_account, ork8s.github_account) (#1231). - Update
devops k8s deploy-stackanddevops k8s renderto ensureconfigmap.yamlis generated from active settings before applying manifests (#1231).
- Add
- GitHub & Cloudflare Published Service Status Monitoring:
- Add
devops gh status,devops cloudflare service-status, and unifieddevops statuscommands to monitor published upstream platform operational status, active incidents, and component health (#1235). - Add Statuspage domain models (
StatuspageSummary,StatuspageComponent,StatuspageStatus,StatuspageIncident) and telemetry client indevops_cli.telemetry.service_status(#1235). - Register
devops_cli_upstream_service_statusanddevops_cli_upstream_component_statusOpenTelemetry/Prometheus instruments, recorded continuously by the metrics adapter indevops 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 onproject-metrics.json(#1235).
- Add
- Keep issues with open or merged pull requests in active releases:
- Update roadmap store pull request queries (
open_pull_requests) to index bothOPENandMERGEDstates via GitHub GraphQL and in-memory store (#1244). - Add
Event.PR_JOINEDandReason.PULL_REQUESTtransitions 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
NeworBlocked, and are excluded from over-cap descoping victims (#1244).
- Update roadmap store pull request queries (
Changed
- Stack lifecycle and release management delegation:
- Update
devops k8s deploy-stackto detect Argo CD-managed clusters, run secret push, and delegate manifest and Helm reconciliation to GitOps (#755). - Guard
devops k8s teardown-stackagainst 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
targetRevisionrewriting to the next open roadmap release duringrelease prepareandrelease cut(#755). - Enforce
Prune=false,Delete=falsesync options across all namespaces and persistent volume claims (#755).
- Update
- 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
mainonly, noting monthly token budget rationale (#840). - Update issue template references in
AGENTS.mdto supported templates (bug_report.yml,feature_request.yml) (#840). - Add
--forceflag todevops ai agents, refusing to overwrite existingAGENTS.mdwithout--forcewhile writing pointer stubs and exiting non-zero (#840). - Remove vestigial
_pointer_stubhelper fromdevops_cli.commands.ai(#840). - Add offline test suite
tests/test_agents_md_claims.pyverifying 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.ymland clarify help text acrossdevops gh branch-protectioncommands to indicate management of classic branch protection rather than rulesets (#842). - Author ADR 0004 recording the CI verification policy, required check names (
Static AnalysisandTests & Coverage), strict mode,update-prs.ymldispatch route, and owner decisions on Copilot reviews (#842). - Add glossary definitions for
Gate,Check, andRequired checktoCONTEXT.md(#842).
- Remove deprecated
- Dependency updates from closed Dependabot pull requests:
- Bump
fastapifrom 0.141.1 to 0.142.2 (#1201). - Bump
gitpythonfrom...
- Bump
v0.2.26
Added
- Coverage Reverse Index Test Selection (
devops ci coverage --build-index,devops ci test):- Added
--build-indexoption todevops ci coverageto generate a per-worktree reverse coverage index (coverage_index.json) mapping source files to covering test files usingCOVERAGE_CORE=ctraceand--cov-context=test(#406). - Enhanced
devops ci testto 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-fallbackrejection support (#406).
- Added
- Ruff Correctness Rule Families (
B,RUF,ASYNC):- Extended Ruff lint selection to whole rule families
B(flake8-bugbear),RUF(Ruff-specific rules), andASYNC(flake8-async), catching runtime bugs, mutable defaults, and unhandled exception chains (#420). - Selected targeted single rules:
SIM115(unclosed file handles),PT011,PT015, andPT017(pytest assertion and raises integrity), andPGH003(explicit type ignore codes) (#420). - Configured
allowed-confusablesfor typographic mathematical and informational glyphs (–,×,ℹ) and enabledignore-without-codein 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).
- Extended Ruff lint selection to whole rule families
- devops-cli in the cluster (
k8s/devops/,devops k8s run-job):- Namespace
devopswith 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 -- ARGScreates a Job from the template with only its args changed, follows its log and exits with its exit code;--dry-runreads nothing, not even the CronJob, and prints the kubectl requests a run would make (#741).
- Namespace
- 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
--rotateto replace a live value (#741). - Server-side apply from stdin: values never reach argv, files, annotations or output (#741).
--dry-runmakes no request, reads included, and prints the requests a push would make in order with placeholders;--planreads 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 authalone: 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).
- One table lists every Secret the stacks read; the command adopts live values, generates the ones nobody types and needs
- 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, FastMCProadmap_intake):- Open issues off the board, unfinished items and new
--title/--body-filecandidates 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/agentand 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-runmakes no request, to GitHub, a model or the telemetry collector, and prints the requests a run makes in order with placeholders, as anIntakePlanmarked as a dry run, each request aPlannedRequest(#741) that shows the exactghcommand 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);--confirmwrites. The FastMCP tool takesmode(planby default,dry-runorconfirm), and a FastMCP tool dispatched in-process with--dry-runexports no telemetry either (#742). - Agent instructions file new work through intake, and
devops gh issues triagereports issues off the board as awaiting intake instead of missing a milestone (#742).
- Open issues off the board, unfinished items and new
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 writesdocs/ROADMAP.mdonchore/cut-vX.Y.Zin the clone at--rootand 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;--planpreviews,--dry-runmakes no request. MCP mirrorroadmap_closepreviews 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 withrefine.needs_split="true"and are descoped by reprioritization. Supports--dry-runand--confirm. Hooks intoapply_intakefor critical fixes and P0 features. Added FastMCP mirror toolroadmap_refineand due table integration indevops 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
ServiceConfigto configuration settings (repos,machine_account,poll_interval_seconds,drain_timeout_seconds) and managed credentialservice.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/githubroute 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/metricsPrometheus endpoints recording delivery outcomes, trigger sources, job counts, job durations, start timestamps, and queue depth (#752).
- Added dedicated continuous service mode (
- Production Service Image for Scheduled Jobs and Webhook Handlers (
Dockerfile,.github/workflows/ci.yml,.github/workflows/release.yml):- Root
Dockerfilemulti-stage build packaging Python 3.14 runtime,ghCLI,git, anddevops-clivirtual environment into a hardened, non-root (1000:1000), read-only container with/etc/gitconfigcredentials (#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-imagejob 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-imagejob publishingghcr.io/dan-petty/devops-cli/service:v<version>and:latestwith SPDX SBOM generation and cryptographic build provenance attestation via GitHub Actions (#753). - Dependabot
dockerecosystem tracking in.github/dependabot.ymland release routine verification instructions indocs/ROUTINE_TASKS.md(#753).
- Root
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 oforigin/release/vX.Y.Zwith session identity. Supports--dry-runand--confirmmodes. Wiresservice_jobadapter todevops serve --servicefor processing coalesced trigger batches. Adds FastMCP mirror toolroadmap_runreporting due jobs (#981).- Roadmap Service Kubernetes Deployment:
- Added single-replica
RecreateDeployment forroadmap-servicein thedevopsnamespace running `serv...
- Added single-replica