feat(harness): add structural workflow guard gates

This commit is contained in:
2026-10-10 01:04:29 +00:00
parent ac3cfdaeb9
commit 3dce776298
8 changed files with 1038 additions and 0 deletions
@@ -123,6 +123,68 @@ class VerificationContractDocumentationTests(unittest.TestCase):
self.assertIn("trusted `harness/.runtime`", section)
self.assertIn("Producer evidence와 reviewer 실행을 구분", section)
def test_workflow_guard_records_reuse_existing_verification_location(self):
text = CACHE.read_text()
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.assertIn('verification/<run-id>', guard)
def test_workflow_guard_readonly_scope_does_not_ban_approved_worker_edits(self):
text = CACHE.read_text()
guard = text.split('### Shared workflow guard gates', 1)[1].split('Issue별 검증 실행 기록의 정본', 1)[0]
self.assertIn('읽기 전용', guard)
self.assertIn('쓰기 worker는 승인된 변경 allowlist', guard)
def test_workflow_guard_snapshot_protects_scope_before_delegation(self):
implementation = SKILLS["implementation"].read_text()
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()
self.assertIn("workflow_guard.py check-tests", rework)
self.assertIn("test inventory", rework)
self.assertIn("RED/GREEN records must be named", rework)
def test_workflow_guard_contract_preservation_precedes_spec_verdict(self):
spec = SKILLS["review"].read_text()
self.assertIn("workflow_guard.py check-contracts", spec)
self.assertIn("baseline inventory", spec)
self.assertIn("Gate failure invalidates workflow evidence", spec)
def test_workflow_guard_nested_schema_is_actionable_and_claims_are_not_authenticated(self):
text = CACHE.read_text()
section = text.split('### Shared workflow guard gates', 1)[1].split('Issue별 검증 실행 기록의 정본', 1)[0]
for token in ('#### Nested input contracts', 'baseline_collection', 'inventory_sha256',
'baseline_status', 'expected_failure', 'replacement_ids', 'kind',
'delete', 'rename', 'status', 'review_evidence', 'scope_sha256',
'expected_exit_code', 'failure_kind', 'semantic_key', 'current_keys',
'observation', 'intentionally_changed', 'sort_keys=True',
'separators', 'UTF-8', 'shape-only', 'one row per baseline semantic key'):
with self.subTest(token=token):
self.assertIn(token, section)
import json
blocks = re.findall(r'```json\n(.*?)```', section, re.S)
self.assertEqual(len(blocks), 2, 'provide runnable test and contract input examples')
self.assertEqual(set(json.loads(blocks[0])), {'baseline_ids', 'current_ids', 'baseline_status',
'current_status', 'baseline_collection', 'current_collection', 'authorized_changes', 'changed_tests', 'findings'})
self.assertEqual(set(json.loads(blocks[1])), {'baseline', 'current', 'mappings', 'stages', 'precedence'})
def test_mapping_schema_types_use_raw_source_not_redacted_tool_rendering(self):
# Authorization-looking prose can be masked by read-tool transport.
# Inspect the approved public schema in raw source; never disable redaction.
declaration = 'authorization: string, verification: string'
masked = 'authorization: ' + '*' * 3 + ' verification'
helper = ROOT / 'harness/lib/workflow_guard.py'
for path in (helper, CACHE):
text = path.read_text()
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()
self.assertNotIn("plan과 manifest를 제외한 clean tree", text)