feat: hard merge gate by default (CI + directory + contract + coordinator + reviews)
- cloud/merge_gate_hard.py (new): pure hard gate; mode resolution is env-only (unset/invalid -> hard, fail-closed); five checks plus branch protection must be protected; unknown/unprotected block even with explicit confirm; no-evidence blocks (fail-closed); directory check reuses #209 check_directory_isolation (v0.3 rule); blocks codes + merge_gate_blocked audit detail are the track-3 pushback interface (no pushback state machine in this track). - cloud/server.py: POST /tasks/{id}/merge uses hard mode by default; record/enforce/off only when explicitly set via env (preserving #209 semantics); body merge_gate_mode is ignored server-side so callers cannot downgrade the gate; every gate block writes a merge_gate_blocked audit before returning 409, status unchanged; hard-mode body type errors return 400. - docs/merge-gate-branch-protection.md (new): protection payload (PR + strict status checks + no force-push + no deletion + enforce_admins), empty required_checks = unconfigured, Pro/public prerequisites, 403 causes; states protection is NOT claimed enabled. - SKILL.md (gate-mechanism wording only): four checks -> five checks; body + coordinator + client appendix gate paragraphs updated to default hard, merge_gate_blocked audit, payload doc reference. - tests: 31 new hard-gate tests (pure + endpoint + skill text); #209 endpoint tests now set record env explicitly (their intent); test_server_merge 200-path calls now carry full hard evidence.
This commit is contained in:
1 parent
d553f9549c
commit
61e08a1a8c
7 files changed
+1048
-39
No files matched your search
@@ -261,6 +261,10 @@ class MergeEndpointGateTestCase(unittest.TestCase):
|
||||
return task
|
||||
|
||||
def test_no_gate_fields_behaves_like_old_endpoint(self):
|
||||
# 本用例测的是 record 模式的过渡语义(#209 原意):默认已是
|
||||
# hard,见 test_merge_gate_hard.py 的默认 409 用例;这里显式
|
||||
# 切 record 才沿用“无证据放行、只附带门禁信息”。
|
||||
os.environ["CLOUD_MERGE_GATE_MODE"] = "record"
|
||||
task = self._completed_task()
|
||||
status, merged = self._req(
|
||||
"POST", "/tasks/%s/merge" % task["id"], {}, self.owner_key)
|
||||
@@ -283,6 +287,8 @@ class MergeEndpointGateTestCase(unittest.TestCase):
|
||||
self.assertEqual(got["status"], "completed")
|
||||
|
||||
def test_unprotected_confirm_allows_and_audits_risk(self):
|
||||
# record 模式语义:硬模式下未保护 + 确认仍阻断,见硬门禁测试。
|
||||
os.environ["CLOUD_MERGE_GATE_MODE"] = "record"
|
||||
task = self._completed_task()
|
||||
status, body = self._req(
|
||||
"POST", "/tasks/%s/merge" % task["id"],
|
||||
|
||||
Reference in new issue
Block a user