Skip to content

chore: add opt-in /passci skill for CI-verified pull requests - #9546

Open
danieljbruce wants to merge 5 commits into
googleapis:mainfrom
danieljbruce:chore/add-passci-skill
Open

danieljbruce wants to merge 5 commits into
googleapis:mainfrom
danieljbruce:chore/add-passci-skill

Conversation

@danieljbruce

@danieljbruce danieljbruce commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds an opt-in /passci agent skill under .agents/skills/passci/ that guides coding agents to open a draft pull request immediately after solving a task (Push #1 of 2), create local [Style Maintenance], [Independent review follow-ups], and [Address CI errors] commits, push all follow-up commits in a single batch (Push #2 of 2) to preserve GitHub Actions quota, and run 1 final "/gemini review" on the PR to confirm no major issues remain.

Impact

Ensures that pull requests created with /passci surface the initial solution in a draft PR right away while systematically enforcing repository style guidelines, two independent Gemini reviews per round within Jetski (up to 5 rounds or until no high-priority issues come up), and 95% unit test confidence (including packages skipped by conditional CI triggers) while requiring only 2 git push invocations so GitHub unit tests do not reach their quota.

Changes

  • Added .agents/skills/passci/SKILL.md defining the 5-stage opt-in /passci workflow with a strict 2-push budget:
    1. Produce the requested code change on a dedicated branch.
    2. Push datastore: benchmark datastore calls against protobuf endpoint #1 of 2: Open a draft pull request immediately so the developer can view the suggested changes before follow-up commits are added.
    3. Reference existing contributing guidelines and coding style documentation (CONTRIBUTING.md, core/packages/gax/CONTRIBUTING.md, the Google TypeScript and JavaScript Style Guides, gts, .eslintrc.json, .prettierrc.cjs, and bin/linter.mjs) and create local style commits prefixed with [Style Maintenance].
    4. Conduct 2 independent Gemini code reviews per round locally within Jetski (using context-isolated subagents) and create local fixes prefixed with [Independent review follow-ups] up to five times or until no high-priority issues come up, whatever comes first.
    5. Verify unit tests pass locally with at least 95% confidence (even when skipped by ci/run_conditional_tests.sh), resolve any local CI/test failures with commits prefixed with [Address CI errors], perform Push Add transactional query, get, put and del #2 of 2 to push all follow-up commits in a single batch, and run 1 final "/gemini review" on the PR to confirm no outstanding major issues remain.
  • Added .agents/skills/passci/scripts/passci.py to deterministically audit CONTRIBUTING.md style compliance, evaluate independent Gemini review rounds for high-priority issues, enforce the 2-push budget, identify CI blind spots, compute 95% confidence unit test plans, and validate commit prefix ordering.

Testing

  • Verified passci.py unit tests covering 95% confidence sample size calculation ($n = 59$), independent Gemini review stopping conditions (5 rounds max or 0 high-priority issues), CI blind spot classification, CONTRIBUTING.md style auditing, and commit prefix ordering.
  • Verified .agents/skills/passci/scripts/passci.py --repo-root . --mode audit against the google-cloud-node repository.

Alternatives

  • Running /gemini review on the PR and pushing after every intermediate commit was considered, but conducting the iterative independent Gemini reviews locally within Jetski (2 reviewers per round, up to 5 rounds) and batching all follow-up commits into a single second push avoids exhausting GitHub Actions unit test quota while still running 1 final /gemini review on the PR to confirm no major issues remain.
  • Not merging this PR would leave agents without a standardized opt-in /passci workflow in googleapis/google-cloud-node.

Adds the public opt-in `/passci` skill under `.agents/skills/passci/` to guide Jetski through opening a draft PR immediately after solving a task and pushing `[Style Maintence]`, `[Independent review follow-ups]`, and `[Address CI errors]` commits with 95% unit test confidence.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the /passci opt-in workflow skill for googleapis/google-cloud-node, adding a detailed markdown guide and a helper Python script to automate style audits, review checks, and unit test verification. The code review identified several critical robustness issues in the helper script, including potential crashes from unhandled exceptions during file reading and JSON parsing of API responses or configuration files. Additionally, a typo in the style maintenance commit prefix was flagged for correction across the codebase and documentation.

Comment thread .agents/skills/passci/scripts/passci.py Outdated
Comment on lines +666 to +671
issue_comments: Sequence[Mapping[str, Any]] = (
json.loads(issue_proc.stdout) if issue_proc.returncode == 0 else []
)
pull_comments: Sequence[Mapping[str, Any]] = (
json.loads(pull_proc.stdout) if pull_proc.returncode == 0 else []
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If the GitHub API returns an error or unexpected non-list response (e.g., {"message": "Not Found"}), json.loads will return a dictionary. Iterating over issue_comments or pull_comments directly will then iterate over the dictionary keys (strings), leading to an AttributeError when calling .get() on lines 677 or 686. This will crash the script. Validate that the parsed JSON is indeed a list before using it.

Suggested change
issue_comments: Sequence[Mapping[str, Any]] = (
json.loads(issue_proc.stdout) if issue_proc.returncode == 0 else []
)
pull_comments: Sequence[Mapping[str, Any]] = (
json.loads(pull_proc.stdout) if pull_proc.returncode == 0 else []
)
issue_json = json.loads(issue_proc.stdout) if issue_proc.returncode == 0 else []
issue_comments: Sequence[Mapping[str, Any]] = (
issue_json if isinstance(issue_json, list) else []
)
pull_json = json.loads(pull_proc.stdout) if pull_proc.returncode == 0 else []
pull_comments: Sequence[Mapping[str, Any]] = (
pull_json if isinstance(pull_json, list) else []
)

@danieljbruce danieljbruce Oct 7, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 5aab6f7: validated that issue_json and pull_json are list instances (isinstance(..., list)) and that each element is a Mapping before iterating.

Comment thread .agents/skills/passci/scripts/passci.py Outdated
Comment on lines +732 to +733
if isinstance(raw_ignore, dict):
ignored_packages = [str(x) for x in raw_ignore.get("ignored", [])]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If ignore.json contains "ignored": null, raw_ignore.get("ignored", []) will return None. Iterating over None in the list comprehension will raise a TypeError and crash the script. Safely check that the value is a list before processing.

Suggested change
if isinstance(raw_ignore, dict):
ignored_packages = [str(x) for x in raw_ignore.get("ignored", [])]
if isinstance(raw_ignore, dict):
ignored_val = raw_ignore.get("ignored")
ignored_packages = (
[str(x) for x in ignored_val] if isinstance(ignored_val, list) else []
)

@danieljbruce danieljbruce Oct 7, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 5aab6f7: checked isinstance(ignored_val, list) on raw_ignore.get("ignored") before iterating so a null value in ignore.json safely defaults to [].

Comment on lines +502 to +505
try:
content = full_path.read_text(encoding="utf-8")
except OSError:
continue

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Reading files with read_text(encoding="utf-8") can raise a UnicodeDecodeError if a file contains invalid UTF-8 sequences (e.g., binary files or legacy fixtures). Since UnicodeDecodeError inherits from ValueError, it will not be caught by except OSError:, causing the audit to crash. Catch ValueError as well to make the file reading robust.

Suggested change
try:
content = full_path.read_text(encoding="utf-8")
except OSError:
continue
try:
content = full_path.read_text(encoding="utf-8")
except (OSError, ValueError):
continue

@danieljbruce danieljbruce Oct 7, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 5aab6f7: updated the exception handler around full_path.read_text(encoding="utf-8") to except (OSError, ValueError): so UnicodeDecodeError is caught cleanly.

Comment thread .agents/skills/passci/scripts/passci.py Outdated
MAX_GEMINI_REVIEW_ROUNDS = 3
GEMINI_REVIEW_TRIGGER_COMMAND = "/gemini review"

STYLE_MAINTENANCE_PREFIX = "[Style Maintence]"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There is a typo in the prefix name: "[Style Maintence]" should be "[Style Maintenance]". Correcting this typo will improve readability and maintainability of the commit prefixes. Please also update the corresponding references in SKILL.md and the script's help text.

Suggested change
STYLE_MAINTENANCE_PREFIX = "[Style Maintence]"
STYLE_MAINTENANCE_PREFIX = "[Style Maintenance]"

@danieljbruce danieljbruce Oct 7, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 5aab6f7 and 7349a3d: updated STYLE_MAINTENANCE_PREFIX to "[Style Maintenance]" across passci.py, SKILL.md, and the script CLI help text.

- Fix `[Style Maintenance]` commit prefix spelling across `passci.py` and `SKILL.md`
- Validate that `gh api` JSON responses are lists before iterating in `_fetch_pr_gemini_reviews`
- Guard against `"ignored": null` in `ignore.json` when reading ignored packages
- Catch `ValueError` (including `UnicodeDecodeError`) alongside `OSError` when reading changed files
- Conduct 2 independent Gemini reviews per round locally within Jetski (up to 3 rounds or until no high-priority issues come up) instead of repeatedly triggering `/gemini review` on the PR
- Enforce a strict 2-push budget (Push #1 when opening the draft PR, Push #2 after all local `[Style Maintenance]`, `[Independent review follow-ups]`, and local `[Address CI errors]` commits are complete) so GitHub Actions unit tests do not reach their quota
- Run 1 final `/gemini review` on the PR at the very end to confirm no outstanding major issues remain
@danieljbruce
danieljbruce marked this pull request as ready for review October 7, 2026 14:58
@danieljbruce
danieljbruce requested a review from a team as a code owner October 7, 2026 14:58
@github-actions
github-actions Bot requested a review from shivanee-p October 7, 2026 14:59

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants