Skip to content

feat(kits): move to devenv-based agent kits - #474

Draft
mogul wants to merge 29 commits into
mainfrom
refactor/acq-devenv-base-image
Draft

mogul wants to merge 29 commits into
mainfrom
refactor/acq-devenv-base-image

Conversation

@mogul

@mogul mogul commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Context

AI-assisted implementation for moving toward the ADR-0030 direction: use a generic devenv-enabled, agent-less base image contract and move agent-specific install/config/startup knowledge into kits. The ADR also records that the base image pattern is not necessarily NixOS: it layers Nix single-user, devenv, and direnv onto a parametrized BASE_IMAGE, so Ubuntu-like bring-your-own images remain viable.

Discussion issue: #473
Related patterns PR: GSA-TTS/agentic-coding-patterns#395
Related patterns follow-up: GSA-TTS/agentic-coding-patterns#418

Closes: #478, #479, #480, #481, #482, #483, #484, #485

#475 remains a separate resource-sizing track and is intentionally not closed by this PR.

Current Status

This PR now includes both the ADR and the incremental implementation work. The latest increments wire neutral kit-owned rc snippets across both backends and harden quickstart-side gates that can be completed before pending patterns artifacts land.

Summary

  • Add ADR-0030 for agent kits on a devenv-enabled, agent-less base image.
  • Add built-in agent kit metadata/inference and related migration gates.
  • Split provider facts from agent config rendering.
  • Add the neutral /home/agent/.rc.d/*.sh shell-snippet contract.
  • Wire the shared rc.d login-profile bridge for msb and sbx.
  • Keep sbx rc.d bridge installation out of startup-bearing kits to avoid the sbx live-extend refusal path.
  • Harden optional OCI-engine support-kit accounting and msb adapter-provisioning suppression before the patterns kit goes live.
  • Improve non-enforcing BYO/devenv doctor diagnostics with tool-version reporting and clearer rc.d warnings.
  • Add disabled-by-default OpenCode kit readiness coverage so a valid future artifact does not change behavior until explicitly enabled.
  • Preserve and reconcile the neutral rc-snippet documentation from docs(kits): document neutral rc snippets #511.

Verification

  • PASS: ./scripts/test-acq-bats (597/597)
  • PASS: ./scripts/test-acq-bats test/bats/66-oci-engine-kit.bats test/bats/71-msb-provision-secrets.bats test/bats/65-doctor-image-contract.bats test/bats/140-opencode-migration-gate.bats test/bats/90-sbx-startup-kit.bats (70/70)
  • PASS: bash -n acq acq.backends/agents.sh acq.backends/common.sh acq.backends/msb.sh acq.backends/sbx.sh scripts/test-acq-lib.sh scripts/test-acq-bats test/bats/helper.bash test/bats/65-doctor-image-contract.bats test/bats/66-oci-engine-kit.bats test/bats/71-msb-provision-secrets.bats test/bats/140-opencode-migration-gate.bats test/bats/90-sbx-startup-kit.bats scripts/verify-backends
  • PASS: printf '%s\n' acq acq.backends/*.sh scripts/test-acq-lib.sh scripts/test-acq-bats test/bats/*.bats test/bats/helper.bash scripts/verify-backends | xargs -r -n8 shellcheck --severity=warning
  • PASS: git diff --check
  • PASS: npm run lint:md (0 issues)
  • PASS: adversarial AI code review completed; actionable findings addressed before push

Rollback

Revert the commits on this branch. The implementation is contained to acq, backend adapters, documentation, verification scripts, and Bats tests.

Security Impact

No secrets are introduced. The rc.d snippets remain kit-owned files and are documented as non-secret shell integration only. The bridge sources only readable *.sh files whose paths pass a conservative character guard, and sbx installs the bridge through a post-create sbx exec rather than a startup-bearing kit. OCI remains opt-in and gated on a valid patterns-side kit before quickstart suppresses adapter-owned msb provisioning.

Review Focus

  • Shell quoting and guest-vs-host expansion in the shared rc.d bridge.
  • sbx create-time bridge installation and exec-readiness handling.
  • msb/sbx parity for kit-owned rc snippets.
  • Optional OCI support-kit boundaries and msb double-provisioning suppression.
  • Documentation accuracy around bash profile behavior and shell-specific constraints.

@basiliskus

Copy link
Copy Markdown
Contributor

Support for option 3. The login.gov data team kit already lives in this model: since patterns#395 we consume the generic devenv image and install team tools through kit startup steps, so an agent-less base plus an agent kit is the next step rather than a change of course. Some points from that experience, mostly aimed at the open questions.

Agent install: pin by content, derive the image from the kit. The cost of installing the agent at create is once per sandbox (acq already skips on command -v and a marker), seconds to a minute through the proxy, and noise next to the image pull, the Nix store seed, and a first devenv build. The real risk is losing the pin: today acq's create-time fallback is npm install -g opencode-ai, which resolves to latest, and the only thing pinning OpenCode for us is that the image happens to bake it. On a devenv base the natural install is nix profile install github:NixOS/nixpkgs/<rev>#<agent>: pinned by nixpkgs rev, content-hashed, prebuilt from the Nix cache, identical on both arches, and the same line for every agent nixpkgs carries (the team kit installs fd/glab/jq this way already). Agents outside nixpkgs use the goose-server pattern, a sha256-pinned release binary. I would write that into the ADR as the agent-kit install contract, and make the published agent-populated image an artifact built by applying the kit, so image and kit cannot drift.

Activation: acq should not run it. direnv allow is a per-user trust decision on a content hash, and a first devenv build for identity-devops is a multi-core, tens-of-minutes job with 20G of scratch that we deliberately run under the user's eyes. devenv up is process-compose for services, not shell activation. The contract that has worked: the base image ships nix, devenv, direnv and the shell hook; the workspace owns .envrc; the human approves once; acq's job ends at giving exec and run a login shell in the primary repo.

Create-time vs run-time needs a stated rule, and resources belongs in it. Create-time is anything the backend must know before boot (egress, volumes, ports); run-time is files, env, and startup steps. Missing from the vocabulary today: CPU and memory. acq sizes msb guests at 2 vCPU / 4G from an environment variable only, and a kit cannot say "my builds need 4 / 8G". This bit us this week (terraform from source, OOM territory at 4G, 4x slower than the sbx template). A neutral resources: field, max-merged across kits, is the same two-halves pattern as volumes:.

Supervision: two models are now in flight. This ADR asks whether devenv's process supervision replaces ad hoc loops; #472 runs services outside the sandbox as gateways; goose-server runs an in-guest supervisor via background: true. Worth deciding one story for in-guest services and one for out-of-guest before the vocabulary grows three ways to run a daemon.

usai-provider: split facts from rendering. The provider facts (endpoint, key env var, model catalog) are one thing; the config format is each agent's. One agent-aware kit that emits every agent's format is the coupling this ADR removes from acq, relocated into a kit. Have usai-provider export the facts and each agent kit render them. Related: acq should never merge agent config files; the team kit relies on OpenCode's own project-layer deep-merge for its permission gate, and that semantic belongs to the agent.

Bring-your-own image contract must say where the Nix store lives. The team kit's /nix volume seed assumes a store baked at /nix and shadows it at boot. A contract that only says "has nix, devenv, direnv" breaks that silently.

Egress merge: union only, and visible. "Later wins" is right for env and files and wrong for network. A personal kit widening egress without a trace is the concrete AC-6 case the ADR gestures at.

Team impact if adopted is small: ACQ_IMAGE points at the agent-less image, OpenCode arrives from the built-in agent kit at create, one TROUBLESHOOTING entry changes. Volumes, tools, skills, and injection steps are untouched.

@mogul

mogul commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

Great feedback, thank you.

Supervision: two models are now in flight. This ADR asks whether devenv's process supervision replaces ad hoc loops; #472 runs services outside the sandbox as gateways; goose-server runs an in-guest supervisor via background: true. Worth deciding one story for in-guest services and one for out-of-guest before the vocabulary grows three ways to run a daemon.

I've pretty much already settled on leveraging podman-compose for the host side since it's cross-platform between Mac/Linux/Windows, already incorporates dependencies, health-checks, restart policies, etc. (I briefly considered also using devenv for the host side, but then realized that while it works in Mac and Linux, it does not work in Windows.)

0028 is claimed by the Windows-native secret storage ADR and 0029 by
another in-flight PR, so the agent-kits-on-devenv ADR takes 0030. Rename
only; content unchanged.

Refs: #464, #474
Records that the patterns devenv image currently ships devenv 1.11.2
(nixos-25.05) and that devenv 2.x first lands in nixos-26.05 (2.1.2) or
nixpkgs-unstable (2.2/2.3), then maps the 2.x changes that affect this
ADR: native process supervision, devenv.nix-keyed activation, out-of-tree
--from/devenv allow, port allocation and the localhost proxy, bundled
SecretSpec, JSON introspection, and the smaller faster footprint.

Refs: GSA-TTS/agentic-coding-patterns#395
Records the review outcomes for the questions the ADR left open: a
content-pinned agent install contract; human-owned activation with an
explicit ACQ_ env opt-in for non-interactive use; a create-time vs
run-time rule plus a max-merged resources: field; the native devenv
process manager as the general in-guest supervisor (host side uses
podman-compose); provider facts exported by usai-provider and rendered by
each agent kit; a /nix-anchored bring-your-own-image contract; union-only
visible egress; a personal kit plus a neutral ~/.rc.d hook with documented
use-cases; built-in-bundle-only agent kit inference; an optional OCI
engine kit; declared privileged capabilities under passwordless sudo;
SecretSpec confined to in-sandbox usage; and the stability contract to
preserve during migration.

Leaves three follow-ups open: merge semantics beyond egress, the exact
minimum image contract, and migration sequencing.

Refs: #473, #474
@mogul

mogul commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor Author

Pushed commits to this branch. They renumber the ADR to 0030 (0028 and 0029 are claimed by other in-flight work), add a Devenv Baseline and Upstream Impact section recording that the patterns devenv image currently ships devenv 1.11.2 on nixos-25.05 while devenv 2.x first appears in nixos-26.05 / nixpkgs-unstable, and fold in the review feedback: a content-pinned agent-install contract; human-owned activation with an explicit ACQ_ opt-in for non-interactive use; a create-time versus run-time rule; the native devenv process manager as the general in-guest supervisor with podman-compose on the host side; usai-provider exporting facts and each agent kit rendering them; a /nix-anchored bring-your-own-image contract; union-only, visible egress; a personal kit plus a neutral ~/.rc.d hook with documented use-cases; built-in-bundle-only agent kit inference; an optional OCI engine kit; privileged capabilities declared and off by default under passwordless sudo; and SecretSpec confined to in-sandbox usage — leaving three follow-ups open (merge semantics beyond egress, the minimum image contract, and migration sequencing).

A scoped follow-up for updating the devenv image itself based on research into 2.x is tracked at GSA-TTS/agentic-coding-patterns#418.

Removes the resources: field ask from the create-time versus run-time
section so guest CPU/memory sizing does not complicate the devenv
adoption story, and tracks the user need separately.

Refs: #475, #474
@mogul mogul changed the title docs(adr): propose devenv-based agent kits feat(kits): move to devenv-based agent kits Sep 17, 2026
Two offline-suite failures on refactor/acq-devenv-base-image:

- 'agent-kit-heal(sbx): forced refresh includes recorded agent kit' broke when
  the selection gate tightened from acq_agent_builtin_kit_enabled to
  acq_agent_builtin_kit_ready (validated artifact). opencode enablement is
  deferred until the pinned patterns bundle ships a valid opencode agent kit,
  so the test now stubs readiness offline to exercise the refresh path without
  falsely enabling opencode.
- 'heal(sbx): the playbook stale-probe uses the AGENTS.md footprint, not .git'
  passed in CI but failed on hosts with a configured git identity, which
  injects a git-identity kit-add. Neutralize the git identity in the probe
  subshell (matching the same fix on #476) so
  the assertion is host-agnostic.

Co-authored-by: OpenCode [claude_4_8_opus] <bret.mogilefsky@gsa.gov>
Introduce ACQ_ENABLE_OCI_KIT (off by default) to select an oci-engine
capability kit into the built-in bundle, per ADR-0030's direction to move
rootless podman provisioning out of backend-specific adapter code into an
explicit, backend-neutral capability kit.

This increment is deliberately additive and conservative:

- Selection is gated on a readiness probe (acq_oci_engine_kit_ready) that
  fetches and validates the kit at the pinned patterns ref. The patterns-side
  kit body is not published yet, so opting in currently falls back with a
  single stderr notice and adds no ref, leaving default behavior and the msb
  adapter's active podman provisioning (_acq_msb_ensure_oci) unchanged.
- The kit is NOT added to ACQ_KIT_NAMES, preserving the default acq kit list
  set and the provenance-bundle count; selection is gated purely in
  _acq_selected_builtin_kit_refs.
- The readiness result is memoized per process (ACQ_OCI_KIT_READY_CACHE) so at
  most one network fetch runs per invocation, and the fallback notice is
  emitted once per process from _build_kit_list (process-global guard),
  mirroring the agent-kit readiness cache and the sbx recreate-notice guard.

The msb adapter's podman provisioning is intentionally left in place; removing
it, wiring the kit into the sbx forced-heal refresh, and sequencing the two OCI
paths are deferred until the patterns kit publishes and the path is
live-verified.

Co-authored-by: OpenCode [claude_4_8_opus] <bret.mogilefsky@gsa.gov>
…ase-image

# Conflicts:
#	acq.backends/msb.sh
Document the kit-owned /home/agent/.rc.d shell snippet contract, shell-specific guardrails, and team-kit migration path.

Refs: #482
@mogul

mogul commented Sep 24, 2026

Copy link
Copy Markdown
Contributor Author

Update pushed: 59d10f0 (feat(kits): wire neutral rc snippets).

What changed:

  • Fast-forwarded this branch through a59a4a4 and preserved/reconciled the neutral rc-snippet docs from docs(kits): document neutral rc snippets #511.
  • Added a shared acq_login_profile_rc_block used by both backend adapters.
  • Wired msb to consume the shared rc.d bridge in its login-profile setup.
  • Added sbx create-time bridge installation through post-create sbx exec, with an exec-readiness wait and without using a startup-bearing kit.
  • Added focused Bats coverage for msb/sbx rc.d behavior, including an executable check that the generated block sources snippets in byte order.
  • Updated docs to describe the implemented bash ~/.profile bridge and shell-specific constraints accurately.

Verification:

  • PASS: ./scripts/test-acq-bats test/bats/72-msb-agent-user.bats test/bats/90-sbx-startup-kit.bats (61/61)
  • PASS: printf '%s\n' acq.backends/common.sh acq.backends/msb.sh acq.backends/sbx.sh | xargs -r -n8 shellcheck --severity=warning
  • PASS: git diff --check
  • PASS: npm run lint:md (0 issues)
  • PASS: adversarial AI code review completed; actionable findings addressed before push

Also updated the PR summary with a fully qualified Closes: line for the implementation-increment issues expected to close when this PR merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(devenv): implement human-owned activation contract

2 participants