agents: refine starlark rules and CI merge skills (#4195)

Document why executable Starlark attributes use allow_files instead of
allow_single_file when accepting multi-output targets like py_binary.
Direct Buildkite status logs to stderr so JSON output pipes cleanly to
jq, and check for active merge queue branches before re-enqueuing PRs.
diff --git a/.agents/rules/starlark.md b/.agents/rules/starlark.md
index d6e322a..bc440b5 100644
--- a/.agents/rules/starlark.md
+++ b/.agents/rules/starlark.md
@@ -33,6 +33,11 @@
 * **Why**: This avoids inlining the normalization as part of a complex
   expression later in the macro expansion.
 
+## `executable = True` Attributes
+* Use `allow_files = True` (with an explanatory comment), not
+  `allow_single_file = True`, when the attribute may accept `py_binary` (whose
+  `DefaultInfo.files` has multiple files).
+
 ## native.test_suite API
 * Don't forward target `tags` to `native.test_suite`; tags on test suites filter
   tests instead of setting target execution behavior.
diff --git a/.agents/skills/buildkite-get-results/scripts/get_buildkite_results.py b/.agents/skills/buildkite-get-results/scripts/get_buildkite_results.py
index 1af829a..e45273a 100755
--- a/.agents/skills/buildkite-get-results/scripts/get_buildkite_results.py
+++ b/.agents/skills/buildkite-get-results/scripts/get_buildkite_results.py
@@ -68,11 +68,14 @@
 
     target = args.pr
     if target.isdigit() and len(target) < 10:
-        print(f"šŸ” Inspecting PR #{target} via gh to find Buildkite URL...")
+        print(
+            f"šŸ” Inspecting PR #{target} via gh to find Buildkite URL...",
+            file=sys.stderr,
+        )
         target = get_build_url_from_pr(target)
 
     build_id = normalize_build_target(target)
-    print(f"šŸš€ Querying Buildkite for build: {build_id}\n")
+    print(f"šŸš€ Querying Buildkite for build: {build_id}\n", file=sys.stderr)
 
     # Run bk build view
     res = subprocess.run(["bk", "build", "view", build_id])
@@ -84,11 +87,12 @@
         sys.exit(res.returncode)
 
     if args.download:
-        print(f"\nšŸ“„ Downloading logs for build: {build_id}")
+        print(f"\nšŸ“„ Downloading logs for build: {build_id}", file=sys.stderr)
         dl_res = subprocess.run(["bk", "build", "download", build_id])
         if dl_res.returncode != 0:
             print(
-                "āš ļø 'bk build download' failed or not supported. Try using 'bk job log <job-id>' for specific jobs."
+                "āš ļø 'bk build download' failed or not supported. Try using 'bk job log <job-id>' for specific jobs.",
+                file=sys.stderr,
             )
 
 
diff --git a/.agents/skills/merge-pr/SKILL.md b/.agents/skills/merge-pr/SKILL.md
index 8be6782..399b329 100644
--- a/.agents/skills/merge-pr/SKILL.md
+++ b/.agents/skills/merge-pr/SKILL.md
@@ -27,9 +27,7 @@
      branch.
 4. **Queue Shepherding**: Periodically check `gh pr view <pr_number> --json
    state,autoMergeRequest,mergeStateStatus,mergeable`. While `state` is
-   `"OPEN"`, ensure auto-merge is enabled / queued by running `gh pr merge
-   <pr_number> --auto --squash`. If `autoMergeRequest` is null (e.g., ejected
-   from the merge queue due to a CI flake in the temporary queue branch),
-   re-enqueue it for merge by running `gh pr merge <pr_number> --auto --squash`
-   once checks are retried or green.
+   `"OPEN"`, if `autoMergeRequest` is `null` and no
+   `gh-readonly-queue/.*/pr-<pr_number>-` branch exists, re-enqueue with
+   `gh pr merge <pr_number> --auto --squash` once checks are green.
 5. **Completion Notification**: Once `state` becomes `"MERGED"`, send a high-priority message back to the parent conversation.