Repository navigation
feat(acq-kits): add opencode agent-harness kit (SHA-pinned release binary) - #455
Conversation
Quickstart's built-in agent-kit readiness gate (acq_validate_agent_builtin_kit_dir
/ kit_spec_agent_field in acq.backends/common.sh, on the in-progress
refactor/acq-devenv-base-image branch) parses a top-level `agent:` block with
`name:`/`entrypoint:` children from a kit's spec.yaml, to confirm a fetched kit
genuinely matches the agent token acq is about to enable it for. The hybrid/v1
schema had no such field and additionalProperties: false, so any kit declaring
agent: failed validate-kits.py outright. Confirmed live before this change:
jsonschema.validate() on a spec with an agent: block raised "Additional
properties are not allowed ('agent' was unexpected)".
This blocks GSA-TTS/agentic-coding-quickstart#485 (the OpenCode agent-kit
migration, part of the ADR-0030 devenv/agent-kits epic tracked on
GSA-TTS/agentic-coding-quickstart#473) before any kit content can even pass
CI, independent of the kit's own correctness. Landing it as its own
small, independently-reviewable change rather than bundled into the kit PR.
Added `agent` as an optional object (required: name, entrypoint; both
kebab-case-pattern strings, additionalProperties: false) matching exactly the
shape the quickstart parser expects.
Verified:
- A spec.yaml with a valid agent: block now validates.
- The schema itself remains valid Draft 2020-12.
- No regression: existing kits without the field (spot-checked: paseo) still
validate unchanged.
- `validate-kits.py --strict` run clean against all 8 real kits on current
main (agentic-coding-playbook, git-ssh-sign, openchamber, paseo,
pi-coding-agent, prime-agent, usai-provider, zscaler-ca-certificate).
Co-authored-by: OpenCode Agent <agent@gsa.gov>
…nary) First harness-agent kit in the family: installs a pinned, SHA-256-verified OpenCode CLI release binary into the sandbox at create time, replacing acq's own unpinned MSB fallback (`npm install -g opencode-ai`, resolves to whatever npm currently calls "latest"). Declares the new top-level `agent:` block (name/entrypoint, schema added in the prerequisite PR) so acq's built-in agent-kit readiness gate can confirm this kit genuinely provides the `opencode` agent token before enabling it. Install-method decision (SHA-pinned release binary, not npm or nixpkgs) was reached via a 3-role consensus_vote before authoring, after live-verifying: nixpkgs at the devenv image's pinned revision resolves `opencode` to v0.3.112 from the pre-rename upstream repo (sst/opencode) — ~70 releases stale — so installing via that pinned nixpkgs would downgrade users rather than deliver current OpenCode. The npm path (`opencode-ai`) has its own documented bug (the sibling quickstart repo's KNOWN_FAILURE_MODES.md): its postinstall step intermittently fails fetching the real binary. This kit's method has no npm metadata and no postinstall step to fail — downloaded, SHA-256-verified, and independently derived both pins (v1.18.34, x64 + arm64) from the real GitHub release assets, not copied from any third party. Integrity posture deliberately diverges from every sibling kit in this family: a SHA-256 mismatch refuses to install (fail CLOSED), rather than degrading to any fallback — see docs/decisions/fail-closed-on-integrity.md. Every other failure path (no pin, unsupported arch, network failure, extraction failure) fails soft, matching the family's convention. Went through two independent adversarial subagent reviews before this commit — a security-focused review and a QA/accuracy/vestigial-content review — and fixed every finding both raised, not just logged them: Security fixes: - HIGH: tar extraction trusted a `[ -f ]` check that follows symlinks: a hash-matching-but-source-compromised release whose `opencode` member is a symlink would have its target's content silently copied, root-owned and world-readable, into /usr/local/bin/opencode. Added an explicit `[ -L ]` rejection before trusting the extracted member. - MEDIUM: the idempotency fast-path resolved `opencode` via a PATH lookup and trusted its self-reported --version with no re-verification — a persisted prior-boot compromise (or another kit's binary earlier on PATH) would never be re-checked. Fixed to check the kit's own fixed destination path directly, never PATH, with the same symlink rejection. - Confirmed non-issue after live testing (not just assumed): the version-pin glob looks exploitable in isolation but is closed by an existing second charset-negation check; no tainted variable reaches a shell-reparsing sink regardless. QA/accuracy fixes: - HIGH (real functional bug): the kit's own scripts/verify called validate-kits.py WITHOUT --root, unlike every sibling kit's verify script — reproduced live that this breaks the kit's own offline gate when run from outside the repo root. Fixed. - HIGH (accuracy): README mischaracterized objects.githubusercontent.com as a git-LFS-only host; sibling kits (openchamber, prime-agent) actually use that same host for the same purpose (release downloads). Rewrote to say so, and reframed the egress-host choice as a documented latent fragility (GitHub has changed release-asset redirect hosts before) rather than an asserted settled fact. - MEDIUM (accuracy): README overclaimed the opencode-ai npm package itself lacks metadata/a postinstall step; it has both — the real point is this kit's own install path never touches npm. Rewrote accordingly. - MEDIUM (doc/code mismatch): spec.yaml and script comments described in-script literal fallback values that don't exist in the code. Rewrote to state accurately that spec.yaml's install command is the single source of truth for the pin, deliberately not duplicated. Verified post-fix (not just pre-fix): validate-kits.py --strict passes 9/9 kits, the kit's own scripts/verify passes 6/6 checks including a live integrity-negative test (wrong hash correctly refuses to install anything), shellcheck clean, markdownlint clean, full repo pytest 401/401 (no regression). A final 3-role consensus_vote sanity pass on the fixed kit was dispatched async before this commit but did not resolve within a reasonable wait; proceeding on the strength of the two completed, finding-fixed adversarial reviews rather than blocking further — the vote's result will be reported and acted on separately if it surfaces anything new. Co-authored-by: OpenCode Agent <agent@gsa.gov>
Address adversarial review findings for the opencode agent-harness kit. Make the offline verifier truly offline, reject unsafe temp-dir fallback, validate archive member type/count before extraction, avoid PATH in the final run check, and add kit troubleshooting documentation. Co-authored-by: OpenCode Agent <agent@gsa.gov>
|
AI-assisted follow-up review and fixes pushed in What was fixed
Adversarial reviewRan adversarial sub-agent review before and after fixes. The first pass found the offline verifier/network dependency, temp-dir fallback, shell syntax checker mismatch, missing inventory/troubleshooting docs, and durable-reference wording. Follow-up passes found and then verified fixes for hardlink/non-regular archive members, final Verification
Notes
|
|
I pushed the fixes above because I thought we needed to land this before landing all of the other stuff around GSA-TTS/agentic-coding-quickstart#474, but I'm uncertain about the state of the provider-kit changes so I'm holding off on pushing a merge through. In the meantime, I'd be happy for someone else to take a good look at the state of this branch and confirm it's good to go! |
|
AI-assisted independent re-verification (OpenCode), confirming @mogul's Pulled the current head and independently re-verified all 4 security fixes — not by trusting the summary, but by building my own test fixtures from scratch and exercising the actual validation logic: 1. Offline verifier is genuinely offline now. Confirmed via diff: the local 2. Temp-dir hardening. Confirmed: the old 3. Archive member-type/count validation — independently stress-tested with my own fixtures. Built a symlink-member archive, a genuine regular-file archive, and (most usefully) a duplicate-member archive with two different-content
4. Final runnability check no longer trusts PATH. Confirmed via diff: Also verified:
This is a genuinely good adversarial-review catch on your part — the archive-layout validation in particular is a real gap neither of my own two earlier reviews considered. Every fix holds up under independent, from-scratch adversarial testing, not just its own author's test cases. I'm confident this branch is good to merge. |
|
IMPORTANT Merging this PR makes the OpenCode harness kit present in State after merge:
So this merge is a safe prerequisite/availability step: it lands the binary-install kit and metadata target for ACQ readiness checks. It should not be read as “the end-to-end provider facts → OpenCode config → runnable configured agent path is complete.” |
Move the built-in patterns bundle from v1.10.0 (2dd2ad6ad5f63b842d732424bed5ba9aebeed676) to v1.11.0 (f2227413df78dc5ef6580b5e29c59542dfe1f66a). Included patterns commits: - 36038a6 feat(acq-kits): add opencode agent-harness kit (SHA-pinned release binary) (GSA-TTS/agentic-coding-patterns#455) - 73606d7 fix(usai-provider): make the catalog drift check report real drift (GSA-TTS/agentic-coding-patterns#459) - 56500ba fix(usai-provider): drop embedding models from the chat-model catalog (GSA-TTS/agentic-coding-patterns#462) The oci-engine kit this PR adds to the opt-in catalog is still present at the new ref, so catalog selection is unchanged. The opencode agent kit becomes fetchable at this pin for the first time, but it stays dormant: acq_agent_builtin_kit_enabled returns false for every agent, so _build_kit_list still yields four built-in kits and no agent kit is applied. Enabling it is separate work tracked by #485. provider-facts/usai.env remains absent at v1.11.0, so the provider-facts loader still returns 2 (artifact absent, tolerated) and the transitional fallback defaults remain the only reachable path. #481 is unaffected. Behavior change riding along with the bump: the usai-provider kit applies to every sandbox, and its model catalog no longer advertises the cohere_english_v3 embedding model (13 lines removed from files/home/usai-config/opencode.jsonc), with 7 lines added to scripts/sync-usai-models.mjs so the drift check reports real drift. Co-authored-by: OpenCode [claude-opus-5] <bret.mogilefsky@gsa.gov>
Move the built-in patterns bundle from v1.10.0 (2dd2ad6ad5f63b842d732424bed5ba9aebeed676) to v1.11.0 (f2227413df78dc5ef6580b5e29c59542dfe1f66a). Included patterns commits: - 36038a6 feat(acq-kits): add opencode agent-harness kit (SHA-pinned release binary) (GSA-TTS/agentic-coding-patterns#455) - 73606d7 fix(usai-provider): make the catalog drift check report real drift (GSA-TTS/agentic-coding-patterns#459) - 56500ba fix(usai-provider): drop embedding models from the chat-model catalog (GSA-TTS/agentic-coding-patterns#462) The oci-engine kit this PR adds to the opt-in catalog is still present at the new ref, so catalog selection is unchanged. The opencode agent kit becomes fetchable at this pin for the first time, but it stays dormant: acq_agent_builtin_kit_enabled returns false for every agent, so _build_kit_list still yields four built-in kits and no agent kit is applied. Enabling it is separate work tracked by #485. provider-facts/usai.env remains absent at v1.11.0, so the provider-facts loader still returns 2 (artifact absent, tolerated) and the transitional fallback defaults remain the only reachable path. #481 is unaffected. Behavior change riding along with the bump: the usai-provider kit applies to every sandbox, and its model catalog no longer advertises the cohere_english_v3 embedding model (13 lines removed from files/home/usai-config/opencode.jsonc), with 7 lines added to scripts/sync-usai-models.mjs so the drift check reports real drift. Co-authored-by: OpenCode [claude-opus-5] <bret.mogilefsky@gsa.gov>
Move the built-in patterns bundle from v1.10.0 (2dd2ad6ad5f63b842d732424bed5ba9aebeed676) to v1.11.0 (f2227413df78dc5ef6580b5e29c59542dfe1f66a). Included patterns commits: - 36038a6 feat(acq-kits): add opencode agent-harness kit (SHA-pinned release binary) (GSA-TTS/agentic-coding-patterns#455) - 73606d7 fix(usai-provider): make the catalog drift check report real drift (GSA-TTS/agentic-coding-patterns#459) - 56500ba fix(usai-provider): drop embedding models from the chat-model catalog (GSA-TTS/agentic-coding-patterns#462) The oci-engine kit this PR adds to the opt-in catalog is still present at the new ref, so catalog selection is unchanged. The opencode agent kit becomes fetchable at this pin for the first time, but it stays dormant: acq_agent_builtin_kit_enabled returns false for every agent, so _build_kit_list still yields four built-in kits and no agent kit is applied. Enabling it is separate work tracked by #485. provider-facts/usai.env remains absent at v1.11.0, so the provider-facts loader still returns 2 (artifact absent, tolerated) and the transitional fallback defaults remain the only reachable path. #481 is unaffected. Behavior change riding along with the bump: the usai-provider kit applies to every sandbox, and its model catalog no longer advertises the cohere_english_v3 embedding model (13 lines removed from files/home/usai-config/opencode.jsonc), with 7 lines added to scripts/sync-usai-models.mjs so the drift check reports real drift. Co-authored-by: OpenCode [claude-opus-5] <bret.mogilefsky@gsa.gov>
Summary
First harness-agent kit in the family: installs a pinned, SHA-256-verified OpenCode CLI release binary into the sandbox at create time, replacing acq's own unpinned MSB fallback (
npm install -g opencode-ai, resolves to whatever npm currently calls "latest"). Declares the new top-levelagent:block (name/entrypoint, schema added in #454, this PR's base) so acq's built-in agent-kit readiness gate (acq_validate_agent_builtin_kit_dir, in the sibling quickstart repo) can confirm this kit genuinely provides theopencodeagent token before enabling it.Part of the ADR-0030 devenv/agent-kits line of work (umbrella PR GSA-TTS/agentic-coding-quickstart#474, tracking issue #473, kit-specific issue #485).
Depends on #454 (adds the
agent:schema block) — this PR targets that branch, notmain, until #454 merges.Install-method decision
Reached via a 3-role
consensus_votebefore authoring (SHA-pinned release binary vs. npm vs. nixpkgs), after live-verifying:opencodepackage resolves tov0.3.112from the pre-rename upstream repo (sst/opencode) — ~70 releases stale. Installing via that pinned nixpkgs would downgrade users rather than deliver current OpenCode.opencode-ai's own postinstall step has a documented, intermittent failure fetching the real binary (sibling quickstart repo'sKNOWN_FAILURE_MODES.md).v1.18.34, x64 + arm64) independently derived from the real GitHub release assets, not copied from any third party.Integrity posture
Deliberately diverges from every sibling kit in this family: a SHA-256 mismatch refuses to install (fails CLOSED), with no fallback to degrade to — see
docs/decisions/fail-closed-on-integrity.md. Every other failure path (no pin, unsupported arch, network failure, extraction failure) fails soft, matching the family's convention.Review process
Two independent adversarial subagent reviews ran against the authored kit before this PR — a security-focused review and a QA/accuracy/vestigial-content review. Every finding both raised was fixed, not just logged:
Security fixes:
tarextraction trusted a[ -f ]check that follows symlinks — a hash-matching-but-source-compromised release whoseopencodemember is a symlink would have its target's content silently copied, root-owned and world-readable, into/usr/local/bin/opencode. Added an explicit[ -L ]rejection before trusting the extracted member.opencodevia aPATHlookup and trusted its self-reported--versionwith no re-verification — a persisted prior-boot compromise would never be re-checked. Fixed to check the kit's own fixed destination path directly, neverPATH, with the same symlink rejection.QA/accuracy fixes:
scripts/verifycalledvalidate-kits.pywithout--root, unlike every sibling kit's verify script — reproduced live that this breaks the kit's own offline gate when run from outside the repo root. Fixed.objects.githubusercontent.comas a git-LFS-only host; sibling kits (openchamber,prime-agent) actually use that same host for the same purpose (release downloads). Rewrote to say so honestly, and reframed the egress-host choice as a documented latent fragility rather than an asserted settled fact.opencode-ainpm package itself lacks metadata/a postinstall step; it has both — the real point is this kit's own install path never touches npm. Rewrote accordingly.spec.yamland script comments described in-script literal fallback values that don't exist in the code. Rewrote to state accurately thatspec.yaml's install command is the single source of truth for the pin.Verification (post-fix, not just pre-fix)
validate-kits.py --strictpasses 9/9 kitsscripts/verifypasses 6/6 checks, including a live integrity-negative test (a deliberately wrong hash correctly refuses to install anything)shellcheckcleanmarkdownlint-cli2cleanpytestsuite: 401/401 (no regression)A final 3-role
consensus_votesanity pass on the fixed kit was dispatched before opening this PR but did not resolve within a reasonable wait; opening on the strength of the two completed, finding-fixed adversarial reviews rather than blocking further. Will report the vote's result separately if it surfaces anything new once it resolves.AI-assisted change (OpenCode).
Co-authored-by: OpenCode Agent agent@gsa.gov