Skip to content

feat(opencode): Tier A — ship the enforcement adapter, proven end to end - #37

Merged
xoai merged 2 commits into
mainfrom
opencode-tier-a-attested
Jul 17, 2026
Merged

feat(opencode): Tier A — ship the enforcement adapter, proven end to end#37
xoai merged 2 commits into
mainfrom
opencode-tier-a-attested

Conversation

@xoai

@xoai xoai commented Jul 17, 2026

Copy link
Copy Markdown
Owner

The opencode port, completed. opencode goes Tier C → Tier A: sage init now ships an enforcement adapter that makes Sage's gates mechanical on opencode, and the contract derives the tier from attested capabilities.

What ships

  • .opencode/plugin/sage.js — bridges opencode's plugin API to Sage's platform-agnostic gate scripts:
    • tool.execute.before → maps the tool (edit/write/apply_patchEdit/Write, paths parsed from patchText; bashBash) → runs the PreToolUse gates → throws on exit 2, and opencode blocks the call.
    • tool.execute.after → runs the PostToolUse recorders (verify-tracker, degradation-log, manifest-sync).
  • The generator copies all 9 gate scripts to .opencode/sage-hooks/ and writes the adapter (verified through a real init).

Proven — capability attestation + a deterministic adapter test

  • Live probe (docs/attestations/opencode-tier-a-2026-07-17.md), each firing logged so nothing-happened ≠ it-happened:
    • pre-tool-veto — spec-gate exit 2 → throw → edit blocked
    • post-tool-eventstool.execute.after fires with the result payload
    • subagent-dispatch — the task tool spawns an independent reviewer session
    • the bonus that matters: hooks fire inside the subagent (the reviewer's own read fired before/after in the child session) → a subagent is not an enforcement escape hatch
  • Deterministic test (setup/adapter-test.mjs, 5/5) drives the real SagePlugin hooks against the real gates — pre-spec veto, spec-approved allow, config self-disable veto, secrets veto, read untouched — with no opencode model backend (which was flaky during the probe). Imports only node builtins, so it's CI-runnable.

Contract

pre-tool-veto / post-tool-events / subagent-dispatchattested with evidence + expiry (release 1.5). contract.py --check passes; derived tier A. session-events stays false — the platform's event hook fires, but the adapter ships no session-init that uses it (don't claim what isn't delivered). STATUS.md, README truth table, and generator output updated.

The debugging that mattered

A live edit "landed" mid-probe — not because the veto failed, but because a prior run's manifest-sync had advanced the cycle past pre-spec, where the gate correctly allows. On a clean pre-spec manifest the gate exits 2 (confirmed directly and in the deterministic test). Instrument the mechanism, not the outcome — the same lesson three times this session.

Honest edges

No native skill discovery on opencode (system skills are inlined into AGENTS.md; Tier A is about enforcement, not discovery). The coarse Bash-evasion matcher in the shared config-gate has a documented residual. codex remains Tier C (its hooks never provably fired).

fastcheck 14/14 · contract.py --check green · adapter-test 5/5.

xoai added 2 commits July 17, 2026 18:21
…7-17)

Finished the 2026-07-12 investigation's open probes. All three Tier-A
capabilities attested with an instrumented transcript (each firing logged, so
nothing-happened vs it-happened is decidable):

  pre-tool-veto      tool.execute.before → spec-gate exit 2 → edit BLOCKED
  post-tool-events   tool.execute.after fires with the result payload
  subagent-dispatch  the task tool spawns an independent reviewer session

Plus the load-bearing bonus: hooks fire INSIDE the dispatched subagent (the
reviewer's own read fired before/after in the child session), so a subagent is
not an enforcement escape hatch — the veto and config-gate apply within it.

Under ADR-11 opencode's capabilities support Tier A. NOT flipping the contract
here: that requires Sage to SHIP the adapter that uses these hooks (the generator
currently writes only AGENTS.md + commands), else the contract would advertise
enforcement sage init does not deliver — the false claim ADR-11 forbids. The
adapter-shipping build is the next step, tracked separately.
The production port. sage init (opencode) now writes .opencode/plugin/sage.js and
copies the gate scripts to .opencode/sage-hooks/. The adapter bridges opencode's
plugin API to Sage's platform-agnostic gates:

  tool.execute.before → map tool (edit/write/apply_patch→Edit/Write, paths parsed
                        from patchText; bash→Bash) → run PreToolUse gates →
                        throw on exit 2 (opencode blocks the call)
  tool.execute.after  → run PostToolUse recorders (verify-tracker, degradation-
                        log, manifest-sync), non-blocking

Proven: capabilities attested with an instrumented transcript
(docs/attestations/opencode-tier-a-2026-07-17.md — veto blocks an edit, after
fires with the result, task dispatches an independent reviewer, and hooks fire
INSIDE the subagent so it is not an escape hatch). The shipped adapter is tested
deterministically against the real gates (setup/adapter-test.mjs, 5/5:
pre-spec veto, spec-approved allow, config self-disable veto, secrets veto,
read untouched) — independent of opencode's model backend, which was flaky
during the probe.

platform.yaml: pre-tool-veto/post-tool-events/subagent-dispatch → attested with
evidence + expiry; derived tier flips C→A (contract --check passes). STATUS.md,
README truth table, and generator output all updated. session-events stays
false — the platform's event hook fires but the adapter ships no session-init
that uses it.

The debugging that mattered: the first live edit 'landed' not because the veto
failed but because a prior run's manifest-sync had advanced the cycle past
pre-spec, where the gate correctly allows. On a clean pre-spec manifest the gate
exits 2 — confirmed directly and in the deterministic test. Instrument the
mechanism, not the outcome.
@xoai
xoai merged commit 501a992 into main Jul 17, 2026
19 checks passed
@xoai
xoai deleted the opencode-tier-a-attested branch July 17, 2026 12:50
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.

1 participant