pactkit-opencode 3.0.4__tar.gz → 3.0.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/PKG-INFO +1 -1
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/pyproject.toml +1 -1
- pactkit_opencode-3.0.5/src/pactkit_opencode/declarations.py +209 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/src/pactkit_opencode/deployer.py +11 -2
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/src/pactkit_opencode.egg-info/PKG-INFO +1 -1
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/src/pactkit_opencode.egg-info/SOURCES.txt +2 -0
- pactkit_opencode-3.0.5/tests/test_step6_self_sufficiency.py +128 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/README.md +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/setup.cfg +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/src/pactkit_opencode/__init__.py +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/src/pactkit_opencode/enforcement.py +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/src/pactkit_opencode.egg-info/dependency_links.txt +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/src/pactkit_opencode.egg-info/entry_points.txt +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/src/pactkit_opencode.egg-info/requires.txt +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/src/pactkit_opencode.egg-info/top_level.txt +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/tests/test_a1_config_recovery.py +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/tests/test_a2_mcp_retired.py +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/tests/test_bug035_opencode_dual_layer.py +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/tests/test_deploy_opencode_parity.py +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/tests/test_gate_plugin_deploy.py +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/tests/test_host_body_snapshot.py +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/tests/test_opencode_enforcement_channel.py +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/tests/test_story069_opencode_format.py +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/tests/test_story070_opencode_compliance.py +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/tests/test_story071_opencode_config_parity.py +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/tests/test_story073_command_model_routing.py +0 -0
- {pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/tests/test_story_work_units.py +0 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""Opencode's own declarations (SPEC-4.0-22 Step 6).
|
|
2
|
+
|
|
3
|
+
These lived in Core's transitional seed (`_CORE_CAPABILITIES`, `_CORE_SECURITY`,
|
|
4
|
+
`_CORE_PRETOOL_SURFACES`, `_CORE_DEPLOYMENT_CLAIMS`) until Step 6. While they
|
|
5
|
+
did, this adapter could not answer for itself: `import pactkit_opencode` read Core's
|
|
6
|
+
table
|
|
7
|
+
for its own identity, so removing the seed broke the import rather than the
|
|
8
|
+
deployment.
|
|
9
|
+
|
|
10
|
+
Core still owns the **vocabulary** (`capability.CAPABILITY_VOCABULARY`), the
|
|
11
|
+
four levels, and the hole-filling rule in `resolve_capabilities`. What a given
|
|
12
|
+
Host scores on them is the Host's own declaration — that is the split
|
|
13
|
+
SPEC-4.0-22 R1 draws, and this module is the Opencode side of it.
|
|
14
|
+
|
|
15
|
+
Every value here was materialised from the seed Core was carrying, so this is a
|
|
16
|
+
move, not a re-measurement. The `Capability.evidence` strings are preserved
|
|
17
|
+
verbatim for the same reason: they are what the seed said, and re-wording them
|
|
18
|
+
here would quietly change what the registry reports.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from pactkit.capability import Capability, CapabilityLevel
|
|
24
|
+
from pactkit.profiles import (
|
|
25
|
+
CLIPolicy,
|
|
26
|
+
DeploymentClaim,
|
|
27
|
+
FormatProfile,
|
|
28
|
+
InputTranslation,
|
|
29
|
+
SecurityDeclaration,
|
|
30
|
+
SecurityFacts,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"CAPABILITIES",
|
|
35
|
+
"DEPLOYMENT_CLAIM",
|
|
36
|
+
"INPUT_TRANSLATION",
|
|
37
|
+
"PRETOOL_SURFACE",
|
|
38
|
+
"PROFILE",
|
|
39
|
+
"SECURITY",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _cap(
|
|
44
|
+
name: str, level: CapabilityLevel, implemented: bool, evidence: str
|
|
45
|
+
) -> Capability:
|
|
46
|
+
return Capability(
|
|
47
|
+
name=name, level=level, implemented=implemented, evidence=evidence
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
#: The Host's profile. Core no longer owns it; this module is the source.
|
|
52
|
+
PROFILE = FormatProfile(
|
|
53
|
+
name='opencode',
|
|
54
|
+
display_name='OpenCode',
|
|
55
|
+
global_config_dir='~/.config/opencode',
|
|
56
|
+
project_config_dir='.opencode',
|
|
57
|
+
skills_dir='~/.config/opencode/skills',
|
|
58
|
+
agents_dir='~/.config/opencode/agents',
|
|
59
|
+
commands_dir='~/.config/opencode/commands',
|
|
60
|
+
rules_dir='~/.config/opencode/rules',
|
|
61
|
+
project_instructions_file='AGENTS.md',
|
|
62
|
+
global_instructions_file='AGENTS.md',
|
|
63
|
+
pactkit_yaml_path='.opencode/pactkit.yaml',
|
|
64
|
+
agent_format='md',
|
|
65
|
+
rules_import_style='inline',
|
|
66
|
+
excluded_agent_fields=frozenset({'memory', 'permissionMode', 'skills'}),
|
|
67
|
+
excluded_commands=frozenset(),
|
|
68
|
+
supports_model_routing=True,
|
|
69
|
+
skills_path_var='~/.config/opencode/skills',
|
|
70
|
+
command_deploy_form='flat',
|
|
71
|
+
agent_dialect='opencode',
|
|
72
|
+
command_dialect='opencode',
|
|
73
|
+
core_bundled=False,
|
|
74
|
+
cli_policy=CLIPolicy.REQUIRED,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
#: What this Host scores on the capability vocabulary.
|
|
78
|
+
CAPABILITIES: dict[str, Capability] = {
|
|
79
|
+
'completion_hook': _cap('completion_hook', CapabilityLevel.DEGRADED, False,
|
|
80
|
+
"unverified — not measured on this Host; absence of the capability is NOT "
|
|
81
|
+
"established (SPEC-4.0-04 R2: unverified is not unavailable)",
|
|
82
|
+
),
|
|
83
|
+
'git_hook': _cap('git_hook', CapabilityLevel.NATIVE, True,
|
|
84
|
+
'shipped (commit_gate pre-commit chain)',
|
|
85
|
+
),
|
|
86
|
+
'global_instruction': _cap('global_instruction', CapabilityLevel.DEGRADED, False,
|
|
87
|
+
"unverified — not measured on this Host; absence of the capability is NOT "
|
|
88
|
+
"established (SPEC-4.0-04 R2: unverified is not unavailable)",
|
|
89
|
+
),
|
|
90
|
+
'native_command': _cap('native_command', CapabilityLevel.NATIVE, True,
|
|
91
|
+
'shipped (generators.deployer command deploy)',
|
|
92
|
+
),
|
|
93
|
+
'native_skill': _cap('native_skill', CapabilityLevel.NATIVE, True,
|
|
94
|
+
'shipped (generators.deployer._deploy_skills)',
|
|
95
|
+
),
|
|
96
|
+
'persistent_session': _cap('persistent_session', CapabilityLevel.DEGRADED, False,
|
|
97
|
+
"unverified — not measured on this Host; absence of the capability is NOT "
|
|
98
|
+
"established (SPEC-4.0-04 R2: unverified is not unavailable)",
|
|
99
|
+
),
|
|
100
|
+
'post_tool_hook': _cap('post_tool_hook', CapabilityLevel.DEGRADED, False,
|
|
101
|
+
"unverified — not measured on this Host; absence of the capability is NOT "
|
|
102
|
+
"established (SPEC-4.0-04 R2: unverified is not unavailable)",
|
|
103
|
+
),
|
|
104
|
+
'pre_tool_hook': _cap('pre_tool_hook', CapabilityLevel.EQUIVALENT, True,
|
|
105
|
+
"shipped as a plugin veto (tool.execute.before) — Equivalent, not Native: it "
|
|
106
|
+
"is PactKit's plugin, not a host facility. End-to-end in a real session "
|
|
107
|
+
"2026-09-18 (docs/guides/opencode-pretool-channel-evidence.md)",
|
|
108
|
+
),
|
|
109
|
+
'project_instruction': _cap('project_instruction', CapabilityLevel.EQUIVALENT, True,
|
|
110
|
+
'shipped (deployed instruction file)',
|
|
111
|
+
),
|
|
112
|
+
'rule_import': _cap('rule_import', CapabilityLevel.DEGRADED, False,
|
|
113
|
+
"unverified — not measured on this Host; absence of the capability is NOT "
|
|
114
|
+
"established (SPEC-4.0-04 R2: unverified is not unavailable)",
|
|
115
|
+
),
|
|
116
|
+
'session_resume': _cap('session_resume', CapabilityLevel.DEGRADED, False,
|
|
117
|
+
"unverified — not measured on this Host; absence of the capability is NOT "
|
|
118
|
+
"established (SPEC-4.0-04 R2: unverified is not unavailable)",
|
|
119
|
+
),
|
|
120
|
+
'shell_execution': _cap('shell_execution', CapabilityLevel.NATIVE, True,
|
|
121
|
+
'shipped (native bash tool)',
|
|
122
|
+
),
|
|
123
|
+
'structured_tool_result': _cap('structured_tool_result', CapabilityLevel.DEGRADED,
|
|
124
|
+
False,
|
|
125
|
+
"unverified — not measured on this Host; absence of the capability is NOT "
|
|
126
|
+
"established (SPEC-4.0-04 R2: unverified is not unavailable)",
|
|
127
|
+
),
|
|
128
|
+
'subagent': _cap('subagent', CapabilityLevel.DEGRADED, False,
|
|
129
|
+
"unverified — not measured on this Host; absence of the capability is NOT "
|
|
130
|
+
"established (SPEC-4.0-04 R2: unverified is not unavailable)",
|
|
131
|
+
),
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
#: Files a gate must treat specially on this Host (SPEC-4.0-22 R8).
|
|
135
|
+
SECURITY = SecurityDeclaration(
|
|
136
|
+
env_capable=SecurityFacts.declared(['opencode.json']),
|
|
137
|
+
protected=SecurityFacts.empty(),
|
|
138
|
+
conditional=SecurityFacts.empty(),
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
#: Which mechanism this Host offers for pre-tool interception, when one
|
|
142
|
+
#: has been measured. `None` means unverified — not "cannot".
|
|
143
|
+
PRETOOL_SURFACE = 'tool.execute.before (plugin; veto by throwing)'
|
|
144
|
+
|
|
145
|
+
#: How this Host executes and where its verification comes from.
|
|
146
|
+
DEPLOYMENT_CLAIM = DeploymentClaim(
|
|
147
|
+
execution_mode='portable',
|
|
148
|
+
verification_source='adapter_native_command_facade',
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
#: Read-side wire-format data (SPEC-4.0-22 Step 4/7).
|
|
152
|
+
#:
|
|
153
|
+
#: **This Host's wire format, not Core's data about it.** Until Step 7 this
|
|
154
|
+
#: lived in Core (`host_payloads.OPENCODE_INPUT`) and was registered into the
|
|
155
|
+
#: registry at import time. That made Core the owner of a Host-specific
|
|
156
|
+
#: format, and it stayed invisible for exactly as long as Core seeded this Host
|
|
157
|
+
#: at import — the moment the seed went, Core's import-time registration raised
|
|
158
|
+
#: `UnknownAdapterError` in every process that had not run entry-point
|
|
159
|
+
#: discovery, and the gate it feeds failed **open** (measured 2026-09-24).
|
|
160
|
+
#:
|
|
161
|
+
#: The Step 6 commit on this branch declared the slot but left it `None`, which
|
|
162
|
+
#: would have been worse than the bug it replaced: with Core's copy deleted,
|
|
163
|
+
#: nothing would recognise an OpenCode payload at all and the whole channel
|
|
164
|
+
#: would fail open. The values below are Core's, moved **verbatim** — this is
|
|
165
|
+
#: a move, not a re-measurement, and re-deriving them would quietly change what
|
|
166
|
+
#: the gate reads.
|
|
167
|
+
#:
|
|
168
|
+
#: Two entries are explicitly *not* evidence, and say so: `patch` has an
|
|
169
|
+
#: unmeasured argument shape (it is listed so that "every write tool is in the
|
|
170
|
+
#: table" holds, and it is refused through `uncovered_write_tools`), and
|
|
171
|
+
#: unknown tools carrying a path-ish argument are treated as writes as a
|
|
172
|
+
#: fallback.
|
|
173
|
+
#:
|
|
174
|
+
#: `tool_map` is **not injective** — `patch` and `write` both canonicalise to
|
|
175
|
+
#: `Write`. The order below is therefore not semantic: Core's reverse lookup
|
|
176
|
+
#: breaks the tie on `uncovered_write_tools` (preferring the verified spelling)
|
|
177
|
+
#: rather than on iteration order, precisely so this declaration can be written
|
|
178
|
+
#: in whatever order reads best.
|
|
179
|
+
INPUT_TRANSLATION: InputTranslation | None = InputTranslation(
|
|
180
|
+
requires_keys=('tool', 'args'),
|
|
181
|
+
excludes_keys=('tool_name',),
|
|
182
|
+
tool_map={
|
|
183
|
+
'bash': 'Bash',
|
|
184
|
+
'edit': 'Edit',
|
|
185
|
+
'patch': 'Write',
|
|
186
|
+
'write': 'Write',
|
|
187
|
+
},
|
|
188
|
+
arg_map={
|
|
189
|
+
'bash': {
|
|
190
|
+
'command': 'command',
|
|
191
|
+
},
|
|
192
|
+
'edit': {
|
|
193
|
+
'filePath': 'file_path',
|
|
194
|
+
'newString': 'new_string',
|
|
195
|
+
'oldString': 'old_string',
|
|
196
|
+
'replaceAll': 'replace_all',
|
|
197
|
+
},
|
|
198
|
+
'patch': {
|
|
199
|
+
'filePath': 'file_path',
|
|
200
|
+
'patchText': 'content',
|
|
201
|
+
},
|
|
202
|
+
'write': {
|
|
203
|
+
'content': 'content',
|
|
204
|
+
'filePath': 'file_path',
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
read_only_tools=frozenset({'glob', 'grep', 'list', 'read', 'task', 'todoread', 'todowrite', 'webfetch'}),
|
|
208
|
+
uncovered_write_tools=frozenset({'patch'}),
|
|
209
|
+
path_arg_keys=('filePath', 'file_path', 'path'))
|
|
@@ -28,7 +28,7 @@ from pactkit.generators.deployer import (
|
|
|
28
28
|
_deploy_rules,
|
|
29
29
|
_deploy_skills,
|
|
30
30
|
)
|
|
31
|
-
from
|
|
31
|
+
from pactkit_opencode import declarations
|
|
32
32
|
from pactkit.utils import atomic_write
|
|
33
33
|
|
|
34
34
|
# All canonical commands deploy. Sprint has a sequential capability fallback.
|
|
@@ -121,7 +121,16 @@ class OpenCodeDeployer(DeployerBase):
|
|
|
121
121
|
registers, nor recommends external services)
|
|
122
122
|
"""
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
# SPEC-4.0-22 Step 6: this Host answers for itself. These were Core's
|
|
125
|
+
# transitional seed until now; `complete_registration` harvests them off the
|
|
126
|
+
# class, so the adapter no longer reads a table Core owns. `input_translation`
|
|
127
|
+
# rides its own lifecycle — it is installed after the registration exists.
|
|
128
|
+
profile = declarations.PROFILE
|
|
129
|
+
capabilities = declarations.CAPABILITIES
|
|
130
|
+
security = declarations.SECURITY
|
|
131
|
+
pretool_surface = declarations.PRETOOL_SURFACE
|
|
132
|
+
deployment_claim = declarations.DEPLOYMENT_CLAIM
|
|
133
|
+
input_translation = declarations.INPUT_TRANSLATION
|
|
125
134
|
|
|
126
135
|
def deploy(self, config=None, target=None):
|
|
127
136
|
opencode_root = Path(target) if target else Path.home() / ".config" / "opencode"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
README.md
|
|
2
2
|
pyproject.toml
|
|
3
3
|
src/pactkit_opencode/__init__.py
|
|
4
|
+
src/pactkit_opencode/declarations.py
|
|
4
5
|
src/pactkit_opencode/deployer.py
|
|
5
6
|
src/pactkit_opencode/enforcement.py
|
|
6
7
|
src/pactkit_opencode.egg-info/PKG-INFO
|
|
@@ -16,6 +17,7 @@ tests/test_deploy_opencode_parity.py
|
|
|
16
17
|
tests/test_gate_plugin_deploy.py
|
|
17
18
|
tests/test_host_body_snapshot.py
|
|
18
19
|
tests/test_opencode_enforcement_channel.py
|
|
20
|
+
tests/test_step6_self_sufficiency.py
|
|
19
21
|
tests/test_story069_opencode_format.py
|
|
20
22
|
tests/test_story070_opencode_compliance.py
|
|
21
23
|
tests/test_story071_opencode_config_parity.py
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""SPEC-4.0-22 Step 6: this adapter answers for itself.
|
|
2
|
+
|
|
3
|
+
The claim under test is not "registration is complete" — it already was, because
|
|
4
|
+
Core's transitional seed filled the same members. The claim is that **every one
|
|
5
|
+
of them comes from the adapter**.
|
|
6
|
+
|
|
7
|
+
The probe removes Core's declaration for this Host and re-seeds, then imports
|
|
8
|
+
the adapter in that state. It does so **without naming Core's tables**: the
|
|
9
|
+
claim does not depend on whether they are called `_CORE_*` (everything
|
|
10
|
+
published up to 3.0.4) or `_BUILTIN_*` (Step 7, where they hold the builtin
|
|
11
|
+
alone). A probe that hardcodes either name is coupled to a Core version — and
|
|
12
|
+
adapter CI installs Core from PyPI, so it would be testing the version it
|
|
13
|
+
happens to pull rather than the adapter.
|
|
14
|
+
|
|
15
|
+
The assertions name **properties**, not literals from the seed — the seed is
|
|
16
|
+
gone, so quoting it would assert the thing being ruled out.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import subprocess
|
|
22
|
+
import sys
|
|
23
|
+
|
|
24
|
+
HOST = "opencode"
|
|
25
|
+
MODULE = "pactkit_opencode"
|
|
26
|
+
|
|
27
|
+
#: Asserts Core declares nothing for this Host, then imports the adapter. Runs
|
|
28
|
+
#: in a fresh interpreter so the registry is exactly what `profiles` leaves
|
|
29
|
+
#: behind — no earlier import of this adapter can have registered it.
|
|
30
|
+
_UNSEED_TEMPLATE = '''
|
|
31
|
+
import sys
|
|
32
|
+
from pactkit import profiles as P
|
|
33
|
+
|
|
34
|
+
# Remove every trace of Core's declaration for this Host — by SHAPE, not by a
|
|
35
|
+
# hardcoded table name. Naming `_CORE_*` breaks on Step 7 core; naming
|
|
36
|
+
# `_BUILTIN_*` breaks on everything published before it. The claim under test
|
|
37
|
+
# does not depend on what the table is called, so the probe must not either.
|
|
38
|
+
removed = [n for n, v in list(vars(P).items())
|
|
39
|
+
if isinstance(v, dict) and HOST in v]
|
|
40
|
+
for n in removed:
|
|
41
|
+
vars(P)[n].pop(HOST, None)
|
|
42
|
+
|
|
43
|
+
# Re-seed through whatever this Core calls its seeder, so the registry is a
|
|
44
|
+
# consistent post-seed state rather than a half-emptied one.
|
|
45
|
+
for seeder in ("_seed_builtin", "_seed_from_core"):
|
|
46
|
+
fn = getattr(P, seeder, None)
|
|
47
|
+
if fn is not None:
|
|
48
|
+
fn()
|
|
49
|
+
break
|
|
50
|
+
|
|
51
|
+
# The precondition the import has to survive — and the guard against a probe
|
|
52
|
+
# that proves nothing. On Core 3.0.4 the removal above is what establishes it;
|
|
53
|
+
# on Step 7 Core it was already true, because there is no seed to remove. Both
|
|
54
|
+
# readings are the same claim, so this assertion is meaningful on both rather
|
|
55
|
+
# than demanding that something was removed (which Step 7 Core would fail for
|
|
56
|
+
# having done its job).
|
|
57
|
+
assert HOST not in P.FORMAT_PROFILES, (
|
|
58
|
+
"Core still carries this Host's profile when the adapter loads"
|
|
59
|
+
)
|
|
60
|
+
assert HOST not in P._ADAPTER_REGISTRY, (
|
|
61
|
+
"this Host is registered before its adapter has loaded"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
import MODULE # noqa: F401 — the import IS the assertion
|
|
65
|
+
|
|
66
|
+
from pactkit.profiles import get_registration
|
|
67
|
+
reg = get_registration(HOST)
|
|
68
|
+
assert reg.complete, "registration incomplete after an unseeded import: " + reg.state
|
|
69
|
+
assert reg.source == P.SOURCE_ENTRY_POINT, "source is " + reg.source
|
|
70
|
+
print("OK")
|
|
71
|
+
'''
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _code(import_adapter: bool = True) -> str:
|
|
75
|
+
"""The probe, optionally without the adapter import (the control).
|
|
76
|
+
|
|
77
|
+
The control substitution happens **before** the MODULE/HOST placeholders are
|
|
78
|
+
filled in. Doing it after silently matched nothing — the literal it looked
|
|
79
|
+
for no longer contained `MODULE` — and the control then ran the full probe
|
|
80
|
+
and passed for the wrong reason. Measured 2026-09-24.
|
|
81
|
+
"""
|
|
82
|
+
code = _UNSEED_TEMPLATE
|
|
83
|
+
if not import_adapter:
|
|
84
|
+
before = code
|
|
85
|
+
code = code.replace(
|
|
86
|
+
"import MODULE # noqa: F401 — the import IS the assertion",
|
|
87
|
+
"pass # control: do NOT import the adapter",
|
|
88
|
+
)
|
|
89
|
+
assert code != before, "the control substitution matched nothing"
|
|
90
|
+
return code.replace("HOST", repr(HOST)).replace("MODULE", MODULE)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _run(code: str) -> subprocess.CompletedProcess:
|
|
94
|
+
return subprocess.run(
|
|
95
|
+
[sys.executable, "-c", code], capture_output=True, text=True, timeout=180
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_the_adapter_stands_up_without_its_core_seed():
|
|
100
|
+
"""The unseeded import is the whole claim — it raised before Step 6."""
|
|
101
|
+
result = _run(_code())
|
|
102
|
+
assert result.returncode == 0, (
|
|
103
|
+
"the adapter could not stand up without Core's transitional seed — "
|
|
104
|
+
"which is exactly what Step 7 deletes:\n" + result.stderr[-2000:]
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_the_probe_moves_when_the_adapter_is_not_imported():
|
|
109
|
+
"""Control: the probe's assertions must distinguish the two states.
|
|
110
|
+
|
|
111
|
+
A control that merely re-runs the probe with one of its own assertions
|
|
112
|
+
disabled proves nothing — it passes because the check was removed, not
|
|
113
|
+
because the probe is sound. (Measured: the first version of this control did
|
|
114
|
+
exactly that and passed for that reason.)
|
|
115
|
+
|
|
116
|
+
The discriminating question is whether the probe's assertions *move*. So
|
|
117
|
+
this runs the same removal **without importing the adapter**: now nothing
|
|
118
|
+
has registered opencode, and the probe must report that — a registration
|
|
119
|
+
that is absent or incomplete, and a host still counted as transitional.
|
|
120
|
+
"""
|
|
121
|
+
probe = _code(import_adapter=False)
|
|
122
|
+
result = _run(probe)
|
|
123
|
+
assert result.returncode != 0, (
|
|
124
|
+
"with no adapter imported, the probe still reported a complete "
|
|
125
|
+
"entry-point registration — so its assertions do not depend on the "
|
|
126
|
+
"adapter at all, and the main test is not measuring self-sufficiency"
|
|
127
|
+
)
|
|
128
|
+
assert "Traceback" in result.stderr, result.stderr[-800:]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/src/pactkit_opencode.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/src/pactkit_opencode.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/src/pactkit_opencode.egg-info/requires.txt
RENAMED
|
File without changes
|
{pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/src/pactkit_opencode.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/tests/test_opencode_enforcement_channel.py
RENAMED
|
File without changes
|
|
File without changes
|
{pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/tests/test_story070_opencode_compliance.py
RENAMED
|
File without changes
|
{pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/tests/test_story071_opencode_config_parity.py
RENAMED
|
File without changes
|
{pactkit_opencode-3.0.4 → pactkit_opencode-3.0.5}/tests/test_story073_command_model_routing.py
RENAMED
|
File without changes
|
|
File without changes
|