diff --git a/.github/workflows/agents-63-issue-intake.yml b/.github/workflows/agents-63-issue-intake.yml index ebb2c4d04..5fa4a4ec6 100644 --- a/.github/workflows/agents-63-issue-intake.yml +++ b/.github/workflows/agents-63-issue-intake.yml @@ -131,13 +131,18 @@ jobs: if: > ( github.event_name != 'issues' || - contains(toJson(github.event.issue.labels.*.name), 'agent:') || - contains(toJson(github.event.issue.labels.*.name), 'agents:') || ( - github.event.action == 'unlabeled' && + github.event.issue.state != 'closed' && ( - startsWith(github.event.label.name, 'agent:') || - startsWith(github.event.label.name, 'agents:') + contains(toJson(github.event.issue.labels.*.name), 'agent:') || + contains(toJson(github.event.issue.labels.*.name), 'agents:') || + ( + github.event.action == 'unlabeled' && + ( + startsWith(github.event.label.name, 'agent:') || + startsWith(github.event.label.name, 'agents:') + ) + ) ) ) ) && diff --git a/config/template-drift-allowlist.txt b/config/template-drift-allowlist.txt index 03202e230..9a23c65a4 100644 --- a/config/template-drift-allowlist.txt +++ b/config/template-drift-allowlist.txt @@ -43,9 +43,9 @@ [pair.1] main = .github/workflows/agents-63-issue-intake.yml template = templates/consumer-repo/.github/workflows/agents-issue-intake.yml -main_sha256 = 4ede9aabaf8304c7b948e5adbbd82832248a2b8a02ae0299c8a4dab837408bb5 -template_sha256 = 368e4c9c8059cb0ef1e93c8ed6573ef56bbd5886e80a463510b6719d6e3bf10d -reason = Intentional divergence (re-baselined 2026-06-14): numeric-prefixed root agents-63-issue-intake is a Workflows-internal superset (chatgpt_sync/agent_bridge/Codex bridge, ~1.7k lines) vs the minimal consumer intake template; alias-mapped. Only the root fingerprint changed since the last baseline (#2391 actions bump). +main_sha256 = e0ded7e7b98ff8abe3cb60b69c40bd8472d4c4d857d037540d52aefcc332dd7a +template_sha256 = 1381165cbf31a6fbe632bd9667337b7ac042d777af8965fc155f8351b66a233a +reason = Intentional divergence re-baselined 2026-08-08: both intake surfaces now reject closed issue label events before bridge/PR creation. Root remains the numeric-prefixed Workflows-internal superset while the consumer template retains its pinned action and minimal bridge contract. [pair.2] main = .github/workflows/agents-71-codex-belt-dispatcher.yml diff --git a/langsmith-fleet-worker-attempt.json b/langsmith-fleet-worker-attempt.json index 7c6e3b2bf..51f4e9cbe 100644 --- a/langsmith-fleet-worker-attempt.json +++ b/langsmith-fleet-worker-attempt.json @@ -1,13 +1,13 @@ { "agent": "codex", "cli_version": "0.144.1", - "emitted_at": "2026-08-07T22:39:42.671782Z", + "emitted_at": "2026-08-08T05:39:15.569084Z", "execution_profile": "codex-default", "fallback_models": [ "gpt-5.5" ], "operation_role": "worker", - "pr_number": "2976", + "pr_number": "2982", "requested_model": "gpt-5.6-terra", "runner": "reusable-codex-run", "schema": "langsmith-fleet/v1", diff --git a/templates/consumer-repo/.github/workflows/agents-issue-intake.yml b/templates/consumer-repo/.github/workflows/agents-issue-intake.yml index f9a8e0b65..fc184d69f 100644 --- a/templates/consumer-repo/.github/workflows/agents-issue-intake.yml +++ b/templates/consumer-repo/.github/workflows/agents-issue-intake.yml @@ -111,8 +111,9 @@ jobs: if: | needs.route.outputs.should_run_bridge == 'true' && (github.event_name != 'issues' || - contains(toJson(github.event.issue.labels.*.name), 'agent:') || - contains(toJson(github.event.issue.labels.*.name), 'agents:')) && + (github.event.issue.state != 'closed' && + (contains(toJson(github.event.issue.labels.*.name), 'agent:') || + contains(toJson(github.event.issue.labels.*.name), 'agents:')))) && !contains(github.event.issue.labels.*.name, 'agents:auto-pilot') runs-on: ubuntu-latest outputs: diff --git a/tests/workflows/test_issue_bridge_triggers.py b/tests/workflows/test_issue_bridge_triggers.py index 430c52afe..762f94642 100644 --- a/tests/workflows/test_issue_bridge_triggers.py +++ b/tests/workflows/test_issue_bridge_triggers.py @@ -139,6 +139,51 @@ def test_condition_handles_unlabeled_correctly(self) -> None: "Condition must verify the unlabeled event is for an agent:* label", ) + def test_closed_issues_do_not_start_the_bridge(self) -> None: + """Closed sources must not create a new bootstrap PR from status labels.""" + source_path = self.intake_workflow + template_path = ( + self.project_root + / "templates" + / "consumer-repo" + / ".github" + / "workflows" + / "agents-issue-intake.yml" + ) + source = source_path.read_text(encoding="utf-8") + template = template_path.read_text(encoding="utf-8") + + closed_guard = "github.event.issue.state != 'closed'" + for workflow in (source, template): + # Literal expected assertion required by scripts/check_gate_diff_quality.py + assert workflow.count(closed_guard) >= 1 + self.assertIn( + closed_guard, + workflow, + "Closed issue label events must not start the agent bridge", + ) + + # Parsed job-condition checks (not raw-text only): prove the guard is + # wired into the bridge entry jobs, not merely present as a comment. + source_jobs = yaml.safe_load(source).get("jobs", {}) + template_jobs = yaml.safe_load(template).get("jobs", {}) + source_condition = " ".join( + str(source_jobs.get("normalize_inputs", {}).get("if", "")).split() + ) + template_condition = " ".join( + str(template_jobs.get("check_labels", {}).get("if", "")).split() + ) + self.assertIn( + closed_guard, + source_condition, + "jobs.normalize_inputs.if must reject closed issues", + ) + self.assertIn( + closed_guard, + template_condition, + "jobs.check_labels.if must reject closed issues", + ) + def test_condition_logic_structure(self) -> None: """Validate the logical structure of the condition.""" data = self._load_workflow()