Repository navigation
feat(opencode): support PrivacyCode as a compatible install target - #27
Conversation
SignalLayerLabs
left a comment
There was a problem hiding this comment.
Thanks, this is the right abstraction.
I agree that PrivacyCode should be a target rather than a separate adapter as long as it is genuinely reusing the OpenCode plugin contract unchanged. Duplicating the adapter here would create divergence for no benefit.
A few things I like in particular:
- keeping
opencodeandprivacycodeas separate engine identities in the ledger; - separate ledger roots and data overrides, so evidence is never pooled across engines;
- binding the engine identity at install time instead of relying on an environment variable;
- allowing both targets to coexist;
- refusing unrecognized
--sourceJavaScript viaPLUGIN_SOURCE_UNRECOGNIZED; - being explicit that compatibility is observed rather than guaranteed.
The last point matters. A target is effectively a claim that two engines expose the same governance surface, and that claim should stay narrow.
I have two requests before merge.
First, I would formalize the target/adapter boundary in code or docs if it is not already explicit:
- a target may vary installation/runtime identity only;
- it must not override event semantics, outcome classification, capabilities, or enforcement behavior;
- if any of those diverge, it becomes a separate adapter.
That invariant will matter if more OpenCode forks get added later. I don't want OpenCodeTarget to slowly become an escape hatch for engine-specific behavior.
Second, the current compatibility check is still mostly version-based plus observed protocol compatibility. That's fine for Observe mode, but I would not let a compatible target inherit future enforcement eligibility automatically from OpenCode.
Even if they share the plugin API, Earned Enforcement evidence should remain engine-specific. PrivacyCode should have to earn its own enforcement window and counterfactual/regression evidence rather than inheriting OpenCode's trust state.
In other words:
same adapter != same trust
The separate engine label/ledger you added already gives us the right foundation for that.
The PLUGIN_SOURCE_UNRECOGNIZED change is also a good hardening improvement. Please make sure marker validation is strict enough that we're checking a MARGINAL-owned artifact rather than just looking for an easily spoofed substring. It doesn't need to become package signing in this PR, but the trust boundary should be documented accurately.
The E2E result looks appropriate for this stage: Observe only, duplicate recommendation recorded, shell outcomes backed by exit codes, nothing blocked, and no command text persisted.
Since this PR is stacked on #26 → #25, I'd still merge in order:
- get #25 green and merge it;
- rebase / merge #26 and validate the OpenCode runtime boundary;
- rebase #27 onto that clean main;
- run the full CI independently for the PrivacyCode delta.
Architecturally I'm in favor of this.
The main rule I want preserved going forward is:
protocol compatibility can be shared; governance evidence and earned authority cannot.
If PrivacyCode diverges from OpenCode at the plugin/event semantics level, it should stop being a target and become its own adapter, exactly as you noted.
PrivacyCode reuses OpenCode's plugin loader unchanged, so the same plugin and the same bridge protocol govern it. A target records only what differs: the executable name, the global configuration directory, and the ledger location. The engine label stays distinct so one ledger never conflates two engines and a later measurement can compare them rather than pool them. Installing binds the copied plugin to its engine, so both engines can be installed at once without interfering, and install now refuses a source file that is not a MARGINAL plugin. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aa18844 to
5bdff93
Compare
Problem
PrivacyCode is an OpenCode fork that reuses OpenCode's plugin loader unchanged. Governing it needs no
second adapter, but it does need MARGINAL to stop assuming one installation layout per plugin API.
Interface
One
OpenCodeTargetentry. A target records only what differs between compatible engines: theexecutable name, the global configuration directory, and the ledger location. Nothing about the
governance contract is per-target.
marginal install privacycode/marginal uninstall privacycode.installer.render_pluginbinds the copied plugin to its engine at install time, so the plugin reportsthe right engine without depending on an environment variable, and both engines can be installed at
once without interfering. The shipped file still defaults to
opencode, so it stays valid as written.Behavior
Capability label: Observe, identical to OpenCode, because it is the same plugin and the same bridge.
The engine label stays distinct. A ledger that pooled two engines could not answer "does governing this
engine help", so
opencodeandprivacycodeget separate labels, separate ledger roots, and separateMARGINAL_<TARGET>_DATAoverrides.Install also now refuses a source file that does not carry MARGINAL's marker
(
PLUGIN_SOURCE_UNRECOGNIZED), which closes the gap where--sourcecould be pointed at arbitraryJavaScript and have it installed as a governance plugin.
Validation
779 passed, 1 skipped;ruffandmypy src/marginalclean. 11 new tests pin what must stay identicaland what must stay distinct, including both engines installed side by side and removal of one leaving the
other in place.
End-to-end against PrivacyCode 1.18.10:
marginal install privacycode, then a session was asked to runecho onetwice and then a failing command. The ledger recordedengine: privacycodeon every record,SHADOW_OVERRIDEwith aDUPLICATE_ACTIONrecommendation on the repeated command (nothing blocked),two proven successes and one proven failure from shell exit codes, and a session summary with no pending
or unmatched actions. Governance latency was 10.5–11.7 ms per decision. The ledger contained no command
text.
Compatibility
Additive. Existing
opencodebehavior, paths, and ledger location are unchanged.Scientific limitations
limited to tools that report an exit code.
OpenCode's plugin API; if it diverges, it stops being a target and becomes a separate adapter. The
version floor (1.18.0) and the
PLUGIN_SUPPORT_UNVERIFIEDreason code are what surface that, andneither detects a silent behavioral change within a supported version.
fork on the strength of one validated session plus captured protocol shapes.