refactor: unify impl operation gates and correction verification
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
"""Read only the explicitly required workflow reference for canonical impl skills."""
|
||||
from pathlib import Path
|
||||
|
||||
NAMES = {"gitea-issue-implementation", "gitea-issue-rework"}
|
||||
REQUIRED = "Required reference: [workflow](references/workflow.md)"
|
||||
|
||||
def read_contract(path):
|
||||
path = Path(path)
|
||||
text = path.read_text(encoding="utf-8")
|
||||
if path.name == "SKILL.md" and path.parent.name in NAMES:
|
||||
if REQUIRED not in text:
|
||||
raise AssertionError("impl skill does not require its canonical workflow reference")
|
||||
reference = path.parent / "references/workflow.md"
|
||||
if reference.is_symlink() or not reference.is_file():
|
||||
raise AssertionError("canonical workflow reference missing or symlinked")
|
||||
text += "\n" + reference.read_text(encoding="utf-8")
|
||||
return text
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Executable consumer declaration contract wiring."""
|
||||
from pathlib import Path
|
||||
from harness.tests.common.impl_contract_docs import read_contract
|
||||
import unittest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
@@ -22,7 +23,7 @@ class DelegationAdoptionContractTests(unittest.TestCase):
|
||||
'review/skills/gitea-code-review',
|
||||
)
|
||||
for path in paths:
|
||||
text = (ROOT / 'harness/agents' / path / 'SKILL.md').read_text()
|
||||
text = read_contract(ROOT / 'harness/agents' / path / 'SKILL.md')
|
||||
with self.subTest(path=path):
|
||||
self.assertIn('require_adoption', text)
|
||||
self.assertIn('mode', text)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Procedural consumer wiring is not proof of live agent execution."""
|
||||
from pathlib import Path
|
||||
from harness.tests.common.impl_contract_docs import read_contract
|
||||
import unittest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
@@ -15,7 +16,7 @@ class DelegationEvidenceContractTests(unittest.TestCase):
|
||||
def test_consumers_require_gate_before_adoption(self):
|
||||
for path in ROLES:
|
||||
with self.subTest(path=path):
|
||||
text = (ROOT / path).read_text()
|
||||
text = read_contract(ROOT / path)
|
||||
for required in ['## Delegation evidence acceptance gate',
|
||||
'delegation_evidence.py', '위임 결과 채택 이전',
|
||||
'main이 독립적으로 고정한 context',
|
||||
|
||||
@@ -7,6 +7,7 @@ import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from harness.tests.common.impl_contract_docs import read_contract
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
POLICY = ROOT / "harness/operations/repository-cache.md"
|
||||
@@ -38,7 +39,7 @@ class GitCredentialRecipeTests(unittest.TestCase):
|
||||
self.assertIn(required, section)
|
||||
policy = POLICY.read_text(encoding="utf-8")
|
||||
self.assertIn("Lock 파일과 임시 credential helper는 실행 role 소유와 mode `0600`을 요구한다", policy)
|
||||
delivery = REWORK.read_text(encoding="utf-8").split("### 7. 기존 PR branch 전달", 1)[1].split("###", 1)[0]
|
||||
delivery = read_contract(REWORK).split("### 7. 기존 PR branch 전달", 1)[1].split("###", 1)[0]
|
||||
self.assertIn("#non-file-git-credential-helper", delivery)
|
||||
self.assertNotIn("필요한 temporary askpass helper", delivery)
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Contract wiring for the read-only inventory source-binding preflight."""
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from harness.tests.common.impl_contract_docs import read_contract
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
@@ -15,7 +16,7 @@ class InventoryBindingDocumentationTests(unittest.TestCase):
|
||||
]
|
||||
for path in paths:
|
||||
with self.subTest(path=path):
|
||||
text = (ROOT / path).read_text()
|
||||
text = read_contract(ROOT / path)
|
||||
self.assertIn('test_inventory_binding.py', text)
|
||||
self.assertIn('check-tests 이전', text)
|
||||
self.assertIn('독립적으로 고정한 revision과 stage', text)
|
||||
|
||||
@@ -7,6 +7,7 @@ import importlib.util
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from harness.tests.common.impl_contract_docs import read_contract
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
@@ -19,7 +20,7 @@ HEADING = '### Conditional pytest evidence recipe'
|
||||
|
||||
class PytestEvidenceRecipeTests(unittest.TestCase):
|
||||
def recipe(self):
|
||||
text = SKILL.read_text()
|
||||
text = read_contract(SKILL)
|
||||
self.assertIn(HEADING, text, 'conditional pytest recipe is missing')
|
||||
section = text.split(HEADING, 1)[1].split('\n### ', 1)[0]
|
||||
return section, json.loads(section.split('```json\n', 1)[1].split('\n```', 1)[0])
|
||||
|
||||
@@ -3,6 +3,7 @@ import subprocess
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from harness.tests.common.impl_contract_docs import read_contract
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
SCRIPT = ROOT / "harness/agents/impl/skills/gitea-issue-rework/scripts/evidence_manifest.py"
|
||||
@@ -29,7 +30,7 @@ class VerificationContractDocumentationTests(unittest.TestCase):
|
||||
self.assertIn("성공 기록을 실패로 덮어쓰지 않는다", text)
|
||||
for path in SKILLS.values():
|
||||
with self.subTest(path=path.name):
|
||||
role = path.read_text(encoding="utf-8")
|
||||
role = read_contract(path)
|
||||
gate = role.split("## Managed-lock broker gate", 1)[1].split("\n## ", 1)[0]
|
||||
self.assertIn("broker-generated", gate)
|
||||
self.assertIn("ready JSON", gate)
|
||||
@@ -38,11 +39,11 @@ class VerificationContractDocumentationTests(unittest.TestCase):
|
||||
|
||||
def test_role_runtime_startup_precedes_and_does_not_replace_admission(self):
|
||||
roles = [path for path in (ROOT / 'harness/agents').rglob('SKILL.md')
|
||||
if 'broker-generated runtime' in path.read_text()]
|
||||
if 'broker-generated runtime' in read_contract(path)]
|
||||
self.assertEqual(len(roles), 6)
|
||||
for path in roles:
|
||||
with self.subTest(role=path.parent.name):
|
||||
gate = path.read_text().split('## Managed-lock broker gate', 1)[1].split('\n## ', 1)[0]
|
||||
gate = read_contract(path).split('## Managed-lock broker gate', 1)[1].split('\n## ', 1)[0]
|
||||
self.assertIn('ready 이후 각 workflow의 진입 검증·승인 조건을 확인한다', gate)
|
||||
self.assertIn('Runtime 생성은 approval이나 worktree admission을 대신하지 않는다', gate)
|
||||
self.assertIn('ready JSON의 `handle`/`socket`', gate)
|
||||
@@ -55,7 +56,7 @@ class VerificationContractDocumentationTests(unittest.TestCase):
|
||||
self.assertIn("PYTHONDONTWRITEBYTECODE", plan["command_env_allowlist"])
|
||||
|
||||
def test_full_suite_tracked_background_reconciles_before_retry(self):
|
||||
text = SKILLS['rework'].read_text()
|
||||
text = read_contract(SKILLS['rework'])
|
||||
heading = '### Tracked bounded background evidence execution'
|
||||
self.assertIn(heading, text)
|
||||
section = text.split(heading, 1)[1].split('\n### ', 1)[0]
|
||||
@@ -78,7 +79,7 @@ class VerificationContractDocumentationTests(unittest.TestCase):
|
||||
self.assertGreater(command["timeout_seconds"], 0)
|
||||
self.assertLessEqual(command["timeout_seconds"], 3600)
|
||||
for path in (CACHE, *SKILLS.values()):
|
||||
text = path.read_text()
|
||||
text = read_contract(path)
|
||||
for token in ("evidence-manifest/v3", "timeout_seconds", "process group",
|
||||
"validate-archived-v2", "TDD RED", "자동 재실행"):
|
||||
with self.subTest(path=str(path), token=token):
|
||||
@@ -92,7 +93,7 @@ class VerificationContractDocumentationTests(unittest.TestCase):
|
||||
def test_all_roles_reference_canonical_external_artifacts(self):
|
||||
for path in (CACHE, *SKILLS.values()):
|
||||
with self.subTest(path=path.name):
|
||||
text = path.read_text(encoding="utf-8")
|
||||
text = read_contract(path)
|
||||
self.assertIn(CANONICAL, text)
|
||||
self.assertIn("evidence_gap", text)
|
||||
self.assertIn("[REDACTED]", text)
|
||||
@@ -100,7 +101,7 @@ class VerificationContractDocumentationTests(unittest.TestCase):
|
||||
|
||||
def test_examples_match_real_helper_cli_not_unsupported_options(self):
|
||||
for path in (SKILLS["implementation"], SKILLS["rework"]):
|
||||
section = path.read_text().split("## Canonical external verification evidence", 1)[1]
|
||||
section = read_contract(path).split("## Canonical external verification evidence", 1)[1]
|
||||
match = re.search(r"```sh\n(.*?)```", section, re.S)
|
||||
assert match is not None
|
||||
block = match.group(1).replace("\\\n", " ")
|
||||
@@ -135,7 +136,7 @@ class VerificationContractDocumentationTests(unittest.TestCase):
|
||||
def test_prepare_contract_binds_root_and_forbids_recursive_parent_creation(self):
|
||||
for path in (CACHE, SKILLS["implementation"], SKILLS["rework"]):
|
||||
with self.subTest(path=path.name):
|
||||
text = path.read_text(encoding="utf-8")
|
||||
text = read_contract(path)
|
||||
self.assertIn("exact harness root", text)
|
||||
self.assertIn("Path.mkdir(parents=True, mode=0o700)", text)
|
||||
self.assertIn("자동 chmod", text)
|
||||
@@ -163,7 +164,7 @@ class VerificationContractDocumentationTests(unittest.TestCase):
|
||||
guard = text.split('### Shared workflow guard gates', 1)[1].split('Issue별 검증 실행 기록의 정본', 1)[0]
|
||||
self.assertNotIn('or verification root', guard)
|
||||
for path in (SKILLS['implementation'], SKILLS['rework']):
|
||||
self.assertNotIn('outside product checkout and verification root', path.read_text())
|
||||
self.assertNotIn('outside product checkout and verification root', read_contract(path))
|
||||
self.assertIn('verification/<run-id>', guard)
|
||||
|
||||
def test_workflow_guard_readonly_scope_does_not_ban_approved_worker_edits(self):
|
||||
@@ -173,13 +174,13 @@ class VerificationContractDocumentationTests(unittest.TestCase):
|
||||
self.assertIn('쓰기 worker는 승인된 변경 allowlist', guard)
|
||||
|
||||
def test_workflow_guard_snapshot_protects_scope_before_delegation(self):
|
||||
implementation = SKILLS["implementation"].read_text()
|
||||
implementation = read_contract(SKILLS["implementation"])
|
||||
self.assertIn("workflow_guard.py snapshot", implementation)
|
||||
self.assertIn("compare-source --repo-root", implementation)
|
||||
self.assertIn("immutable protected scope", implementation)
|
||||
|
||||
def test_workflow_guard_inventory_is_checked_before_adopting_results(self):
|
||||
rework = SKILLS["rework"].read_text()
|
||||
rework = read_contract(SKILLS["rework"])
|
||||
self.assertIn("workflow_guard.py check-tests", rework)
|
||||
self.assertIn("test inventory", rework)
|
||||
self.assertIn("RED/GREEN records must be named", rework)
|
||||
@@ -215,13 +216,13 @@ class VerificationContractDocumentationTests(unittest.TestCase):
|
||||
masked = 'authorization: ' + '*' * 3 + ' verification'
|
||||
helper = ROOT / 'harness/lib/workflow_guard.py'
|
||||
for path in (helper, CACHE):
|
||||
text = path.read_text()
|
||||
text = read_contract(path)
|
||||
with self.subTest(path=path.name):
|
||||
self.assertEqual(text.count(declaration), 1)
|
||||
self.assertNotIn(masked, text)
|
||||
|
||||
def test_rework_no_longer_excludes_repo_evidence_from_clean_tree(self):
|
||||
text = SKILLS["rework"].read_text()
|
||||
text = read_contract(SKILLS["rework"])
|
||||
self.assertNotIn("plan과 manifest를 제외한 clean tree", text)
|
||||
self.assertIn("새 evidence는 checkout 밖", text)
|
||||
self.assertIn("archived v1 read-only", text)
|
||||
|
||||
Reference in New Issue
Block a user