feat(harness): add structural workflow guard gates
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user