From 4e3b19da5b47910d0f04eb21a144a0b1ed1ee8bf Mon Sep 17 00:00:00 2001 From: Marius Ciepluch <11855163+norandom@users.noreply.github.com> Date: Sun, 13 Sep 2026 18:43:13 +0200 Subject: [PATCH] feat(pre-push-safety-net): cover combined push failure paths --- .../implementation-group-8.md | 37 ++++++ .kiro/specs/pre-push-safety-net/spec.json | 19 ++- .kiro/specs/pre-push-safety-net/tasks.md | 2 +- tests/test_push_regression_integration.py | 122 ++++++++++++++++++ 4 files changed, 173 insertions(+), 7 deletions(-) create mode 100644 .kiro/specs/pre-push-safety-net/implementation-group-8.md create mode 100644 tests/test_push_regression_integration.py diff --git a/.kiro/specs/pre-push-safety-net/implementation-group-8.md b/.kiro/specs/pre-push-safety-net/implementation-group-8.md new file mode 100644 index 0000000..8fe43bf --- /dev/null +++ b/.kiro/specs/pre-push-safety-net/implementation-group-8.md @@ -0,0 +1,37 @@ +# Group 8 implementation and qualification + +The maintainer authorized group 8 on 2026-09-13. Fresh agent dispatch failed with +`agent thread limit reached`; the explicit manual Kiro fallback applies. Reviews +are main-context reviews, not independent reviewer evidence. Joint rollout gates +remain unchanged; implementation progress does not imply capability eligibility. + +## Task 8.1 brief + +Requirements 1.1–1.4, 2.2, 2.3, 4.2, 4.4, 5.2, 5.5, 7.1, 7.4 and 8.2; +design: Snapshot Adapter, Cache/Engine Boundary, Push runner and Result/Hook Adapter. +Combine immutable non-HEAD/tag/force/new-branch/deletion updates, unusual paths, +external boundaries, physical vendor exclusion, census failure and interrupted report +publication. Use production transaction/backend seams; preserve source/index/refs, +truthful scope and prior artifacts, and prove worker cleanup. Tests only: no production +behavior or feature toggle introduced. Existing focused suites retain the full cache, +missing-base, hook-exit and ordinary-scan regressions. + +## Status Report +- STATUS: READY_FOR_REVIEW +- TASK: 8.1 +- RED_PHASE_OUTPUT: N/A: tests-only integration coverage; no production behavior changed. +- TESTS_RUN: Three added combined controls pass. Full suite 1299 passed, 9 skipped in 117.83s; ruff, formatting and mypy pass. + +## Review Verdict +- VERDICT: APPROVED +- TASK: 8.1 +- MECHANICAL_RESULTS: actual new tests inspected, full regression suite passes, no placeholders or secrets; test-only integration boundary maintained. +- FINDINGS: mixed push control verifies all three exact comparisons and four dispositions, readable snapshot bytes, live state preservation and external-content exclusion. Failed census control inspects physically filtered source before returning missing answers. Interrupted writer control checks previous bytes and absence of its process PID. +- SUMMARY: Combined failures remain explicit and do not masquerade as clean checks. + +## Verification Result +- STATUS: VERIFIED +- CLAIM_TYPE: TASK +- CLAIM: Task 8.1 integration regression coverage delivered. +- EVIDENCE: `/tmp/group8-1-focused.log`: 3 passed; `/tmp/group8-1-full.log`: 1299 passed, 9 skipped; static checks exit 0. +- GAPS: These controlled failures do not qualify real detector capabilities or representative latency. diff --git a/.kiro/specs/pre-push-safety-net/spec.json b/.kiro/specs/pre-push-safety-net/spec.json index 6a51723..c9a53f3 100644 --- a/.kiro/specs/pre-push-safety-net/spec.json +++ b/.kiro/specs/pre-push-safety-net/spec.json @@ -1,7 +1,7 @@ { "feature_name": "pre-push-safety-net", "created_at": "2026-09-12T15:35:00Z", - "updated_at": "2026-09-13T16:03:14.583834+00:00", + "updated_at": "2026-09-13T16:41:21.889949+00:00", "language": "en", "phase": "implementation", "approvals": { @@ -19,7 +19,7 @@ } }, "ready_for_implementation": true, - "implementation_approval_note": "Groups 1\u20137 implemented and verified (23/27). Group 7 authorized as a whole; manual Kiro review fallback after fresh dispatch thread limit. Multi-ref input, explicit hook integration and optional bounded witness selection verified. Default capability registry remains empty and rollout remains NO-GO; group 8 pending.", + "implementation_approval_note": "Group 8 authorized and in progress; groups 1\u20137 verified. Capability admission and rollout remain NO-GO until the declared evidence passes.", "depends_on": [ "contributor-scan" ], @@ -75,6 +75,11 @@ "at": "2026-09-13T15:45:15.902603+00:00", "source": "ok, continue with group 7 as a whole", "action": "Authorized tasks 7.1\u20137.3; manual Kiro fallback after fresh dispatch hit thread limit. Group 8 and rollout remain pending." + }, + { + "at": "2026-09-13T16:41:21.889949+00:00", + "source": "move on to group 8", + "action": "Authorized tasks 8.1\u20138.4 and their qualification work; preserve joint gates and record NO-GO wherever prerequisites or measurements do not pass. Fresh dispatch hit thread limit; manual Kiro fallback." } ], "task_review": { @@ -110,10 +115,11 @@ "6.4", "7.1", "7.2", - "7.3" + "7.3", + "8.1" ], - "remaining_tasks": 4, - "verification_record": "implementation-group-7.md", + "remaining_tasks": 3, + "verification_record": "implementation-group-8.md", "verification_records": [ "implementation-group-1.md", "implementation-group-2.md", @@ -121,7 +127,8 @@ "implementation-group-4.md", "implementation-group-5.md", "implementation-group-6.md", - "implementation-group-7.md" + "implementation-group-7.md", + "implementation-group-8.md" ], "next_group": 8, "blocked_tasks": [], diff --git a/.kiro/specs/pre-push-safety-net/tasks.md b/.kiro/specs/pre-push-safety-net/tasks.md index c3b8d99..fb2e2d5 100644 --- a/.kiro/specs/pre-push-safety-net/tasks.md +++ b/.kiro/specs/pre-push-safety-net/tasks.md @@ -212,7 +212,7 @@ independent capability qualification. Groups 7–8 remain pending; rollout remai - _Requirements: 3.5, 4.1, 4.2, 7.1, 7.2, 7.3_ - [ ] 8. Validate usefulness, runtime and cross-language admission -- [ ] 8.1 Exercise complete push failure and regression paths +- [x] 8.1 Exercise complete push failure and regression paths - Combine dirty/non-HEAD/multi-ref/new-branch/force/tag/deletion pushes with unusual paths, external content boundaries, failed census, missing comparisons and interrupted persistence. - Verify no orphan children, no overwritten live state, no vendor code in graphs, exact scope reports and no false clean result from skipped work. - Done when the integration suite covers the critical flows through the real CLI/backend seams and preserves ordinary scan behavior. diff --git a/tests/test_push_regression_integration.py b/tests/test_push_regression_integration.py new file mode 100644 index 0000000..eeea8b4 --- /dev/null +++ b/tests/test_push_regression_integration.py @@ -0,0 +1,122 @@ +"""Combined transaction controls, not detector qualification.""" + +import json +import multiprocessing +import os +import time + +from test_push_runner import history + +from openultrasast.config import PushConfig +from openultrasast.cpg.backend import NullBackend +from openultrasast.push.runner import push + + +def test_mixed_transaction_retains_exact_objects_and_external_boundaries(tmp_path): + root, base, head, git = history(tmp_path) + git("tag", "-a", "release", "-m", "release", head) + tag = git("rev-parse", "release") + # Non-HEAD push while the live tree contains unrelated unusual paths and a symlink. + path = root / "space ü\tname.js" + path.write_text("function route(req) { return req.body; }\n") + (root / "outside.js").symlink_to(tmp_path / "outside-secret") + (tmp_path / "outside-secret").write_text("must never be read by analysis") + git("add", "--", path.name, "outside.js") + git("commit", "-m", "external boundary") + tip = git("rev-parse", "HEAD") + before = (root / ".git/index").read_bytes(), git("show-ref"), git("status", "--porcelain") + zero = "0" * 40 + lines = ( + f"refs/tags/release {tag} refs/tags/release {base}\n" + f"refs/heads/rewind {base} refs/heads/rewind {head}\n" + f"refs/heads/new {tip} refs/heads/new {zero}\n" + f"(delete) {zero} refs/heads/deleted {head}\n" + ) + artifact = tmp_path / "mixed.json" + result = push( + root, + updates=lines, + remote_name="origin", + remote_url="/local", + artifact=artifact, + backend=NullBackend(), + config=PushConfig(comparison_base=base), + ) + data = json.loads(artifact.read_text()) + assert len(data["resolution"]["updates"]) == 4 + assert len(result.result.analyses) == 3 + assert [(a.comparison.head_oid, a.comparison.base_oid) for a in result.result.analyses] == [(head, base), (base, head), (tip, base)] + assert result.result.coverage_status != "complete_within_scope" + assert any(m["boundaries"] for m in data["snapshots"]) + assert all(m["bytes_read"] > 0 for m in data["snapshots"]) + assert "must never be read by analysis" not in artifact.read_text() + assert ((root / ".git/index").read_bytes(), git("show-ref"), git("status", "--porcelain")) == before + + +def test_interrupted_writer_preserves_previous_artifact_and_reaps_child(tmp_path, monkeypatch): + import openultrasast.push.report as reporting + + root, base, head, _ = history(tmp_path) + artifact = tmp_path / "result.json" + artifact.write_text("previous completed artifact") + pidfile = tmp_path / "writer.pid" + + def interrupted(*args): + pidfile.write_text(str(os.getpid())) + time.sleep(10) + + monkeypatch.setattr(reporting, "_write_artifact", interrupted) + before = {p.pid for p in multiprocessing.active_children()} + start = time.monotonic() + result = push( + root, + updates="", + remote_name="origin", + remote_url="/local", + artifact=artifact, + config=PushConfig(deadline_seconds=0.2, cancellation_allowance_seconds=0.4), + ) + assert time.monotonic() - start < 0.8 + assert result.error == "report_deadline_exhausted" + assert artifact.read_text() == "previous completed artifact" + assert result.result.coverage_status == "not_applicable" + assert {p.pid for p in multiprocessing.active_children()} == before + assert pidfile.exists() + assert not os.path.exists("/proc/" + pidfile.read_text()) + + +def test_failed_census_after_physical_vendor_filter_is_not_clean(tmp_path): + from openultrasast.cpg.backend import CpgResult + + root, base, head, git = history(tmp_path) + vendor = root / "node_modules/dependency" + vendor.mkdir(parents=True) + (vendor / "vendor.js").write_text("function vendor(req) { eval(req.body); }\n") + git("add", "--", "node_modules") + git("commit", "-m", "vendor boundary") + tip = git("rev-parse", "HEAD") + reads = [] + + class FailedCensus: + def build(self, source, **kwargs): + files = [p for p in source.rglob("*") if p.is_file()] + assert files + assert not any("node_modules" in p.parts for p in files) + reads.extend(len(p.read_bytes()) for p in files) + return CpgResult(tmp_path / "graph", lambda *_: None, lambda *_: {}) + + artifact = tmp_path / "census.json" + result = push( + root, + updates=f"refs/heads/main {tip} refs/heads/main {base}\n", + remote_name="origin", + remote_url="/local", + artifact=artifact, + backend=FailedCensus(), + ) + data = json.loads(artifact.read_text()) + assert reads and all(n > 0 for n in reads) + assert result.result.coverage_status in ("unavailable", "incomplete") + assert result.result.finding_status == "none" + assert data["admission"]["coverage_reasons"] + assert not any(a.completed_questions for a in result.result.analyses)