its-magic 0.1.3-0 → 0.1.3-2
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.
- package/installer.ps1 +8 -0
- package/installer.py +8 -0
- package/installer.sh +1 -0
- package/package.json +1 -1
- package/scripts/check_intake_template_parity.py +62 -0
- package/template/.cursor/agents/curator.mdc +1 -0
- package/template/.cursor/agents/po.mdc +1 -0
- package/template/.cursor/agents/release.mdc +1 -0
- package/template/.cursor/commands/auto.md +90 -0
- package/template/.cursor/commands/execute.md +26 -0
- package/template/.cursor/commands/refresh-context.md +32 -0
- package/template/.cursor/commands/sovereign-critic.md +104 -0
- package/template/.cursor/model-catalog.local.example.cursor-only.json +9 -0
- package/template/.cursor/model-catalog.local.example.json +9 -0
- package/template/.cursor/model-catalog.local.example.level-1-easy.json +9 -0
- package/template/.cursor/model-catalog.local.example.level-2-complex.json +9 -0
- package/template/.cursor/model-catalog.local.example.level-3-mega.json +9 -0
- package/template/.cursor/model-catalog.local.example.level-4-super.json +9 -0
- package/template/.cursor/model-catalog.local.example.role-based-balanced.json +18 -0
- package/template/.cursor/model-catalog.local.example.role-based-highend.json +18 -0
- package/template/.cursor/rules/sovereign-role-manifest.mdc.example +27 -0
- package/template/.cursor/scratchpad.local.example.md +55 -0
- package/template/.cursor/scratchpad.md +203 -0
- package/template/.cursor/sovereign-role-manifest.yaml.example +53 -0
- package/template/decisions/DEC-0104.md +279 -0
- package/template/decisions/DEC-0105.md +231 -0
- package/template/decisions/DEC-0107.md +246 -0
- package/template/docs/engineering/architecture.md +78 -0
- package/template/docs/engineering/auto-orchestration-reference.md +7 -0
- package/template/docs/engineering/context/installer-owned-paths.manifest +8 -0
- package/template/docs/engineering/reason_codes.md +411 -0
- package/template/docs/engineering/runbook.md +1119 -0
- package/template/docs/engineering/sovereign-memory/.gitkeep +0 -0
- package/template/docs/engineering/sovereign-memory/retrospectives/.gitkeep +0 -0
- package/template/handoffs/sovereign_decisions/.gitkeep +0 -0
- package/template/handoffs/sovereign_deferrals/.gitkeep +0 -0
- package/template/handoffs/sovereign_role_reviews.jsonl +0 -0
- package/template/scripts/__pycache__/decision_ledger_lib.cpython-312.pyc +0 -0
- package/template/scripts/check_intake_template_parity.py +181 -0
- package/template/scripts/decision_ledger_lib.py +732 -0
- package/template/scripts/ledger_validate.py +153 -0
- package/template/scripts/model_tier_lib.py +680 -0
- package/template/scripts/model_tier_validate.py +368 -0
- package/template/scripts/parallel_dev_arbiter.py +923 -0
- package/template/scripts/release_trigger_adapters.py +843 -0
- package/template/scripts/self_healing_deploy_lib.py +463 -0
- package/template/scripts/self_healing_deploy_validate.py +78 -0
- package/template/scripts/sovereign_convergence_lib.py +994 -0
- package/template/scripts/sovereign_convergence_validate.py +206 -0
- package/template/scripts/sovereign_critic_lib.py +629 -0
- package/template/scripts/sovereign_critic_validate.py +131 -0
- package/template/scripts/sovereign_loop_lib.py +828 -0
- package/template/scripts/sovereign_loop_validate.py +122 -0
- package/template/scripts/sovereign_memory_lib.py +869 -0
- package/template/scripts/sovereign_memory_validate.py +153 -0
- package/template/scripts/sovereign_role_manifest_lib.py +547 -0
- package/template/scripts/sovereign_role_manifest_validate.py +105 -0
- package/template/tests/us0108_contract_test.py +207 -0
- package/template/tests/us0109_contract_test.py +209 -0
- package/template/tests/us0109_us0110_compose_test.py +34 -0
- package/template/tests/us0111_contract_test.py +345 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"""US-0111: Twelve `test_us0111_*` contract tests for Release Trigger Adapters.
|
|
2
|
+
|
|
3
|
+
DEC-0111 §7: adapter registry, GitHub webhook, npm publish, Git tag push,
|
|
4
|
+
manual /release, version comparison, atomic promotion, per-version notes,
|
|
5
|
+
ledger event, 9 reason codes, compose guards (US-0100, US-0054).
|
|
6
|
+
|
|
7
|
+
Default source: RELEASE_TRIGGER_SOURCE=manual (zero behavior change vs pre-US-0111).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
import tempfile
|
|
16
|
+
import unittest
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _repo_root() -> Path:
|
|
21
|
+
return Path(__file__).resolve().parents[1]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _load_lib():
|
|
25
|
+
root = _repo_root()
|
|
26
|
+
scripts_dir = str(root / "scripts")
|
|
27
|
+
if scripts_dir not in sys.path:
|
|
28
|
+
sys.path.insert(0, scripts_dir)
|
|
29
|
+
import release_trigger_adapters as mod # noqa: E402
|
|
30
|
+
return mod
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# --- T-001: Adapter registry dispatch (AC-1) ---------------------------------
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class US0111AdapterRegistryDispatchTest(unittest.TestCase):
|
|
37
|
+
"""test_us0111_adapter_registry_dispatch (AC-1)."""
|
|
38
|
+
|
|
39
|
+
def test_us0111_adapter_registry_dispatch(self) -> None:
|
|
40
|
+
lib = _load_lib()
|
|
41
|
+
registry = lib._ADAPTER_MAP
|
|
42
|
+
self.assertIn("manual", registry)
|
|
43
|
+
self.assertIn("github", registry)
|
|
44
|
+
self.assertIn("npm", registry)
|
|
45
|
+
self.assertIn("git_tag", registry)
|
|
46
|
+
self.assertEqual(len(registry), 4)
|
|
47
|
+
|
|
48
|
+
# Dispatch to manual adapter
|
|
49
|
+
ctx = lib.dispatch_to_adapter("manual", env_vars={}, current_version="1.2.3")
|
|
50
|
+
self.assertEqual(ctx.source, "manual")
|
|
51
|
+
self.assertEqual(ctx.version, "1.2.3")
|
|
52
|
+
self.assertIsNone(ctx.previous_version)
|
|
53
|
+
|
|
54
|
+
# Invalid source → fail-closed
|
|
55
|
+
with self.assertRaises(lib.ReleaseTriggerError) as cm:
|
|
56
|
+
lib.dispatch_to_adapter("invalid_source", env_vars={})
|
|
57
|
+
self.assertEqual(cm.exception.code, "RELEASE_TRIGGER_SOURCE_INVALID")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# --- T-002: GitHub webhook adapter (AC-2) ------------------------------------
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class US0111GithubAdapterTest(unittest.TestCase):
|
|
64
|
+
"""test_us0111_github_adapter_success_fail_closed (AC-2)."""
|
|
65
|
+
|
|
66
|
+
def test_us0111_github_adapter_success_fail_closed(self) -> None:
|
|
67
|
+
lib = _load_lib()
|
|
68
|
+
# Success path: valid payload with tag_name
|
|
69
|
+
payload = {"release": {"tag_name": "v1.2.3"}}
|
|
70
|
+
env = {"GITHUB_TOKEN": "test-token", "GITHUB_REPOSITORY": "owner/repo"}
|
|
71
|
+
adapter = lib.GithubReleaseAdapter(env_vars=env, payload=payload, timeout_sec=5)
|
|
72
|
+
ctx = adapter.get_version_info()
|
|
73
|
+
self.assertEqual(ctx.source, "github")
|
|
74
|
+
self.assertEqual(ctx.version, "1.2.3")
|
|
75
|
+
self.assertIn("GITHUB_TOKEN", ctx.metadata.get("token_env_ref", ""))
|
|
76
|
+
|
|
77
|
+
# Fail-closed: missing tag_name
|
|
78
|
+
adapter_bad = lib.GithubReleaseAdapter(env_vars={}, payload={}, timeout_sec=5)
|
|
79
|
+
with self.assertRaises(lib.ReleaseTriggerError) as cm:
|
|
80
|
+
adapter_bad.get_version_info()
|
|
81
|
+
self.assertEqual(cm.exception.code, "RELEASE_TRIGGER_TAG_MISSING")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# --- T-003: npm publish adapter (AC-3) ---------------------------------------
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class US0111NpmAdapterTest(unittest.TestCase):
|
|
88
|
+
"""test_us0111_npm_adapter_success_fail_closed (AC-3)."""
|
|
89
|
+
|
|
90
|
+
def test_us0111_npm_adapter_success_fail_closed(self) -> None:
|
|
91
|
+
lib = _load_lib()
|
|
92
|
+
# Success path: npm_package_version env var
|
|
93
|
+
env = {"npm_package_version": "2.0.1", "npm_package_name": "test-pkg"}
|
|
94
|
+
adapter = lib.NpmPublishAdapter(env_vars=env, timeout_sec=5)
|
|
95
|
+
ctx = adapter.get_version_info()
|
|
96
|
+
self.assertEqual(ctx.source, "npm")
|
|
97
|
+
self.assertEqual(ctx.version, "2.0.1")
|
|
98
|
+
|
|
99
|
+
# Fail-closed: missing package.json + env var
|
|
100
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
101
|
+
adapter_bad = lib.NpmPublishAdapter(
|
|
102
|
+
env_vars={}, timeout_sec=5, repo_root=tmp
|
|
103
|
+
)
|
|
104
|
+
with self.assertRaises(lib.ReleaseTriggerError) as cm:
|
|
105
|
+
adapter_bad.get_version_info()
|
|
106
|
+
self.assertEqual(cm.exception.code, "RELEASE_TRIGGER_PACKAGE_JSON_MISSING")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# --- T-004: Git tag push adapter (AC-4) --------------------------------------
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class US0111GitTagAdapterTest(unittest.TestCase):
|
|
113
|
+
"""test_us0111_git_tag_adapter_success_fail_closed (AC-4)."""
|
|
114
|
+
|
|
115
|
+
def test_us0111_git_tag_adapter_success_fail_closed(self) -> None:
|
|
116
|
+
lib = _load_lib()
|
|
117
|
+
# Success path: GITHUB_REF with semver tag
|
|
118
|
+
env = {"GITHUB_REF": "refs/tags/v3.1.4"}
|
|
119
|
+
adapter = lib.GitTagAdapter(env_vars=env, repo_root=".")
|
|
120
|
+
ctx = adapter.get_version_info()
|
|
121
|
+
self.assertEqual(ctx.source, "git_tag")
|
|
122
|
+
self.assertEqual(ctx.version, "3.1.4")
|
|
123
|
+
|
|
124
|
+
# Fail-closed: no tag resolvable — use tempdir without .git so both
|
|
125
|
+
# GITHUB_REF detection and `git describe` fail.
|
|
126
|
+
with tempfile.TemporaryDirectory() as tmp_empty:
|
|
127
|
+
adapter_bad = lib.GitTagAdapter(env_vars={}, repo_root=tmp_empty)
|
|
128
|
+
with self.assertRaises(lib.ReleaseTriggerError) as cm:
|
|
129
|
+
adapter_bad.get_version_info()
|
|
130
|
+
self.assertEqual(cm.exception.code, "RELEASE_TRIGGER_TAG_MISSING")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# --- T-005: Manual backward compatibility (AC-5) -----------------------------
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class US0111ManualBackwardCompatTest(unittest.TestCase):
|
|
137
|
+
"""test_us0111_manual_backward_compat_byte_identical (AC-5)."""
|
|
138
|
+
|
|
139
|
+
def test_us0111_manual_backward_compat_byte_identical(self) -> None:
|
|
140
|
+
lib = _load_lib()
|
|
141
|
+
# Manual adapter: byte-identical to pre-US-0111 /release behavior
|
|
142
|
+
ctx = lib.dispatch_to_adapter("manual", env_vars={}, current_version="1.0.0")
|
|
143
|
+
self.assertEqual(ctx.source, "manual")
|
|
144
|
+
self.assertEqual(ctx.version, "1.0.0")
|
|
145
|
+
self.assertIsNone(ctx.previous_version)
|
|
146
|
+
self.assertTrue(ctx.metadata.get("manual"))
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# --- T-006: Version comparison logic (AC-6) ----------------------------------
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class US0111CompareVersionsIntegrationTest(unittest.TestCase):
|
|
153
|
+
"""test_us0111_compare_versions_from_trigger_integration (AC-6)."""
|
|
154
|
+
|
|
155
|
+
def test_us0111_compare_versions_from_trigger_integration(self) -> None:
|
|
156
|
+
lib = _load_lib()
|
|
157
|
+
trigger = lib.TriggerContext(
|
|
158
|
+
version="1.2.3",
|
|
159
|
+
previous_version="1.2.2",
|
|
160
|
+
source="github",
|
|
161
|
+
metadata={},
|
|
162
|
+
)
|
|
163
|
+
norm_current, norm_previous = lib.compare_versions_from_trigger(trigger)
|
|
164
|
+
self.assertEqual(norm_current, "1.2.3")
|
|
165
|
+
self.assertEqual(norm_previous, "1.2.2")
|
|
166
|
+
|
|
167
|
+
# Invalid version → fail-closed
|
|
168
|
+
trigger_bad = lib.TriggerContext(
|
|
169
|
+
version="invalid",
|
|
170
|
+
previous_version=None,
|
|
171
|
+
source="manual",
|
|
172
|
+
metadata={},
|
|
173
|
+
)
|
|
174
|
+
with self.assertRaises(lib.ReleaseTriggerError) as cm:
|
|
175
|
+
lib.compare_versions_from_trigger(trigger_bad)
|
|
176
|
+
self.assertEqual(cm.exception.code, "RELEASE_TRIGGER_COMPARE_VERSIONS_FAILED")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# --- T-007: Atomic promotion (AC-7) ------------------------------------------
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class US0111AtomicPromotionTest(unittest.TestCase):
|
|
183
|
+
"""test_us0111_atomic_promotion_temp_rename (AC-7)."""
|
|
184
|
+
|
|
185
|
+
def test_us0111_atomic_promotion_temp_rename(self) -> None:
|
|
186
|
+
lib = _load_lib()
|
|
187
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
188
|
+
target = os.path.join(tmp, "test.txt")
|
|
189
|
+
lib.atomic_write_file(target, "content")
|
|
190
|
+
self.assertTrue(os.path.exists(target))
|
|
191
|
+
with open(target, "r", encoding="utf-8") as f:
|
|
192
|
+
self.assertEqual(f.read(), "content")
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
# --- T-008: Per-version notes atomic write (AC-8) ----------------------------
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class US0111PerVersionNotesTest(unittest.TestCase):
|
|
199
|
+
"""test_us0111_per_version_notes_atomic_write (AC-8)."""
|
|
200
|
+
|
|
201
|
+
def test_us0111_per_version_notes_atomic_write(self) -> None:
|
|
202
|
+
lib = _load_lib()
|
|
203
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
204
|
+
repo = Path(tmp)
|
|
205
|
+
(repo / "handoffs" / "releases").mkdir(parents=True)
|
|
206
|
+
# Mock release_changelog_lib to avoid full dependency
|
|
207
|
+
sys.path.insert(0, str(Path(__file__).parent.parent / "scripts"))
|
|
208
|
+
from release_changelog_lib import normalize_semver, version_fingerprint
|
|
209
|
+
|
|
210
|
+
norm = normalize_semver("1.0.0")
|
|
211
|
+
fp = version_fingerprint(norm, ["US-0111"])
|
|
212
|
+
target = repo / "handoffs" / "releases" / "1.0.0-release-notes.md"
|
|
213
|
+
content = f"# Release 1.0.0\n<!-- fingerprint: {fp} -->\n"
|
|
214
|
+
lib.atomic_write_file(str(target), content)
|
|
215
|
+
self.assertTrue(target.exists())
|
|
216
|
+
self.assertIn("Release 1.0.0", target.read_text(encoding="utf-8"))
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
# --- T-009: Sovereign loop integration (AC-9) --------------------------------
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class US0111LedgerEventTest(unittest.TestCase):
|
|
223
|
+
"""test_us0111_ledger_event_emit_shape (AC-9)."""
|
|
224
|
+
|
|
225
|
+
def test_us0111_ledger_event_emit_shape(self) -> None:
|
|
226
|
+
lib = _load_lib()
|
|
227
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
228
|
+
repo = Path(tmp)
|
|
229
|
+
(repo / "handoffs" / "release_events").mkdir(parents=True)
|
|
230
|
+
trigger = lib.TriggerContext(
|
|
231
|
+
version="1.0.0",
|
|
232
|
+
previous_version="0.9.0",
|
|
233
|
+
source="github",
|
|
234
|
+
metadata={"tag": "v1.0.0"},
|
|
235
|
+
)
|
|
236
|
+
# Mock ledger append to avoid schema validation
|
|
237
|
+
sys.path.insert(0, str(Path(__file__).parent.parent / "scripts"))
|
|
238
|
+
import decision_ledger_lib as ledger_lib
|
|
239
|
+
|
|
240
|
+
original_append = ledger_lib.append_entry
|
|
241
|
+
|
|
242
|
+
def mock_append(*args, **kwargs):
|
|
243
|
+
return ledger_lib.AppendResult(
|
|
244
|
+
success=True,
|
|
245
|
+
reason_code="LEDGER_DISABLED",
|
|
246
|
+
reason_message="mock",
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
ledger_lib.append_entry = mock_append
|
|
250
|
+
try:
|
|
251
|
+
result = lib.emit_version_derivation_event(
|
|
252
|
+
trigger, "1.0.0", "0.9.0", str(repo), scratchpad={}
|
|
253
|
+
)
|
|
254
|
+
self.assertIn("event_path", result)
|
|
255
|
+
event_file = Path(result["event_path"])
|
|
256
|
+
self.assertTrue(event_file.exists())
|
|
257
|
+
event_data = json.loads(event_file.read_text(encoding="utf-8"))
|
|
258
|
+
self.assertEqual(event_data["semver"], "1.0.0")
|
|
259
|
+
self.assertEqual(event_data["previous_semver"], "0.9.0")
|
|
260
|
+
self.assertEqual(event_data["source"], "github")
|
|
261
|
+
finally:
|
|
262
|
+
ledger_lib.append_entry = original_append
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
# --- T-010: Fail-closed reason codes (AC-10) ---------------------------------
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class US0111ReasonCodeInventoryTest(unittest.TestCase):
|
|
269
|
+
"""test_us0111_reason_code_inventory_9_codes (AC-10)."""
|
|
270
|
+
|
|
271
|
+
def test_us0111_reason_code_inventory_9_codes(self) -> None:
|
|
272
|
+
lib = _load_lib()
|
|
273
|
+
# 9 reason codes in FAIL_CODES tuple
|
|
274
|
+
self.assertEqual(len(lib.FAIL_CODES), 9)
|
|
275
|
+
expected_codes = {
|
|
276
|
+
"RELEASE_TRIGGER_ADAPTER_FAILED",
|
|
277
|
+
"RELEASE_TRIGGER_TAG_MISSING",
|
|
278
|
+
"RELEASE_TRIGGER_PREVIOUS_MISSING",
|
|
279
|
+
"RELEASE_TRIGGER_PACKAGE_JSON_MISSING",
|
|
280
|
+
"RELEASE_TRIGGER_ATOMIC_PROMOTION_FAILED",
|
|
281
|
+
"RELEASE_TRIGGER_NOTES_WRITE_FAILED",
|
|
282
|
+
"RELEASE_TRIGGER_EVENT_EMIT_FAILED",
|
|
283
|
+
"RELEASE_TRIGGER_COMPARE_VERSIONS_FAILED",
|
|
284
|
+
"RELEASE_TRIGGER_SOURCE_INVALID",
|
|
285
|
+
}
|
|
286
|
+
self.assertEqual(set(lib.FAIL_CODES), expected_codes)
|
|
287
|
+
|
|
288
|
+
# Reason codes documented in reason_codes.md
|
|
289
|
+
root = _repo_root()
|
|
290
|
+
reason_codes_path = root / "docs" / "engineering" / "reason_codes.md"
|
|
291
|
+
text = reason_codes_path.read_text(encoding="utf-8")
|
|
292
|
+
self.assertIn("US-0111 — Release trigger adapter family", text)
|
|
293
|
+
self.assertIn("RELEASE_TRIGGER_* (9 codes)", text)
|
|
294
|
+
for code in expected_codes:
|
|
295
|
+
self.assertIn(code, text, f"missing {code} in reason_codes.md")
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
# --- T-010: Compose guard US-0100 (no API change) ----------------------------
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
class US0111US0100ComposeTest(unittest.TestCase):
|
|
302
|
+
"""test_us0111_us0100_compose_no_derivation_semantics_change (AC-6 compose guard)."""
|
|
303
|
+
|
|
304
|
+
def test_us0111_us0100_compose_no_derivation_semantics_change(self) -> None:
|
|
305
|
+
# Verify release_changelog_lib APIs unchanged (US-0100 compose guard)
|
|
306
|
+
sys.path.insert(0, str(_repo_root() / "scripts"))
|
|
307
|
+
import release_changelog_lib
|
|
308
|
+
import inspect
|
|
309
|
+
|
|
310
|
+
# normalize_semver(raw) — existing API, unchanged
|
|
311
|
+
sig = inspect.signature(release_changelog_lib.normalize_semver)
|
|
312
|
+
self.assertEqual(len(sig.parameters), 1)
|
|
313
|
+
|
|
314
|
+
# promote_unreleased(semver, sprint_ids, repo_root, release_date) — unchanged
|
|
315
|
+
sig2 = inspect.signature(release_changelog_lib.promote_unreleased)
|
|
316
|
+
self.assertGreaterEqual(len(sig2.parameters), 3)
|
|
317
|
+
|
|
318
|
+
# build_version_doc(semver, sprint_ids, repo_root) — unchanged
|
|
319
|
+
sig3 = inspect.signature(release_changelog_lib.build_version_doc)
|
|
320
|
+
self.assertEqual(len(sig3.parameters), 3)
|
|
321
|
+
|
|
322
|
+
# version_fingerprint(semver, work_item_ids) — unchanged
|
|
323
|
+
sig4 = inspect.signature(release_changelog_lib.version_fingerprint)
|
|
324
|
+
self.assertEqual(len(sig4.parameters), 2)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
# --- T-010: Compose guard US-0054 (no publish semantics change) --------------
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class US0111US0054ComposeTest(unittest.TestCase):
|
|
331
|
+
"""test_us0111_us0054_compose_no_publish_semantics_change (AC-5 compose guard)."""
|
|
332
|
+
|
|
333
|
+
def test_us0111_us0054_compose_no_publish_semantics_change(self) -> None:
|
|
334
|
+
# Manual adapter produces same TriggerContext as pre-US-0111 /release
|
|
335
|
+
lib = _load_lib()
|
|
336
|
+
ctx = lib.dispatch_to_adapter("manual", env_vars={}, current_version="1.0.0")
|
|
337
|
+
self.assertEqual(ctx.source, "manual")
|
|
338
|
+
self.assertEqual(ctx.version, "1.0.0")
|
|
339
|
+
self.assertIsNone(ctx.previous_version)
|
|
340
|
+
# No publish logic in adapter — US-0054 release-all.sh unchanged
|
|
341
|
+
self.assertNotIn("publish", ctx.metadata)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
if __name__ == "__main__":
|
|
345
|
+
unittest.main()
|