From 1f4b4ece21400de982c9116adfe015ccbe388f42 Mon Sep 17 00:00:00 2001 From: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com> Date: Thu, 22 Oct 2026 16:46:28 +0200 Subject: [PATCH 1/2] docs(skills): fold the first Gemini pilot's friction into gemini-task The first agy run (#12224, #12228) went through, with seven frictions that Gemini's report-back and the take-over found: - a bare `gh issue view` and `gh pr edit` fail on this repository (Projects classic); the skill now uses `--json` and `gh api`; - the issue's "change only" list missed `warning_codes.py`, which an autouse fixture forces for every new parser warning; issues now name the files the gates couple, and such a file is in scope for Gemini; - the changelog check reads commits only, as CI does; the skill says so, and the tool now names a fragment that is on disk but not committed; - a fresh venv lacked the `agents` extra; gemini-task and verify-core now give one sync line with the extras CI and the gates use; - the Acceptance section had no case where the existing rules must still win, so a rule built to spec overrode a decimal-comma file; issues for rule changes now list such cases; - the shared checkout's stale skills described the old take-over; the take-over now fast-forwards it first; - Gemini's hand-over needed rewriting into the PR format; it now writes that format directly. It also records the owner's wish of 2026-10-22 that Claude propose this route itself for longer tasks that fit. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M --- .claude/skills/gemini-task/SKILL.md | 95 +++++++++++++++++++------ .claude/skills/verify-core/SKILL.md | 9 ++- changelog.d/gemini-task-friction.md | 9 +++ tests/unit/tools/test_changelog_tool.py | 10 +++ tools/changelog/__init__.py | 15 +++- 5 files changed, 112 insertions(+), 26 deletions(-) create mode 100644 changelog.d/gemini-task-friction.md diff --git a/.claude/skills/gemini-task/SKILL.md b/.claude/skills/gemini-task/SKILL.md index 02bc5872b1f..5337e9d879a 100644 --- a/.claude/skills/gemini-task/SKILL.md +++ b/.claude/skills/gemini-task/SKILL.md @@ -35,6 +35,8 @@ second opinions call Gemini through Vertex AI instead (`/zweitmeinung`). **Good Gemini tasks** need no credentials and no production access, can be verified by commands that run locally, and are worth an hour of work or more. Claude does a quick fix directly; the hand-over costs more than it saves there. +Propose this route yourself: when planning turns up a longer task that fits, +offer it to the owner as an option (owner, 2026-10-22). The longer Gemini is to run, the more planning goes in first: for a large task, get a second opinion on the plan (`/zweitmeinung`) and write the owner's decisions into the issue. Examples: @@ -55,6 +57,19 @@ decisions into the issue. Examples: - Never write `@claude` in it: the util-claude workflow reacts to that mention. - Never put secrets or personal data in it. The repository is public. +- For a change to a rule or heuristic, Acceptance also lists cases where the + existing rules must still win, not only the new rule's positive and negative + cases. #12224 listed none, and Gemini built a currency rule to spec that + overrode a German file's own decimal-comma column; only the independent + review caught it. +- Constraints name the files the gates make Gemini touch as well. For example, + a new parser warning needs its code in `agents/anyplot/data/warning_codes.py` + and a sample in its test, because an autouse fixture in + `tests/unit/agents/data/conftest.py` fails every test that emits an unknown + warning. A "change only" list without them contradicts the gates. +- Wherever you tell Gemini or the owner to read an issue, write + `gh issue view --json title,body,comments`: a bare `gh issue view` + and `gh pr edit` fail on this repository (Projects classic). - Put the body in a private temp file, then create the issue: ```bash @@ -74,10 +89,12 @@ Repo-relative paths, related PRs or issues, and the facts Gemini cannot look up. - [ ] One checkbox per reviewable step; a long refactor gets several. ## Constraints -Scope boundaries, what must not change, and the repository rules that matter most here. +Scope boundaries, what must not change, the files the gates make you touch as +well, and the repository rules that matter most here. ## Acceptance -The exact commands that must pass, and the behavior to check by hand, if any. +The exact commands that must pass, the cases where the existing rules must still +win, and the behavior to check by hand, if any. ## Decide yourself / report back The routine choices Gemini makes and documents, and the questions it returns @@ -120,9 +137,11 @@ the issue. This section is for the Gemini agent in the owner's agy session. -1. **Read the task.** Use `gh issue view `; with no number given, list - the open tasks with `gh issue list --label gemini --state open` and ask the - owner which one to take. Reading GitHub is fine. Your only writes are your +1. **Read the task.** Use `gh issue view --json title,body,comments` + (without `--json` it fails on this repository: Projects classic); with no + number given, list the open tasks with + `gh issue list --label gemini --state open` and ask the owner which one to + take. Reading GitHub is fine. Your only writes are your own branch and your own draft PR (step 7); never create, edit, comment on, label, or close anything else. 2. **Work in the dedicated clone on your own branch.** That clone is @@ -135,6 +154,11 @@ This section is for the Gemini agent in the owner's agy session. git switch -c gemini/- origin/main ``` + In a fresh clone, or after `pyproject.toml` or `uv.lock` changed, sync the + environment once with the extras CI and the gates use; without `agents` the + tests under `tests/unit/agents/` fail at collection: + `uv sync --extra test --extra dev --extra typecheck --extra agents`. + To continue a task from an earlier session, switch to its branch and bring in what Claude may have pushed to it with `git pull --ff-only origin gemini/-`. Then run `git status`, @@ -154,17 +178,22 @@ This section is for the Gemini agent in the owner's agy session. `AGENTS.md` apply: English everywhere, files edited with your edit tool, no lint-disable comments, and documentation updated with behavior. A change that is not exempt adds a changelog fragment under `changelog.d/` without - any `(#...)` reference; Claude adds the PR number later. + any `(#...)` reference; Claude adds the PR number later. A file the gates + make you change as well, such as a registry a test fixture enforces, is in + scope even when the issue's Constraints miss it; name it in the hand-over. 5. **Run the acceptance commands and the gates for what you changed:** ```bash PYTHON_DOTENV_DISABLED=1 uv run ruff check . && uv run ruff format --check . PYTHON_DOTENV_DISABLED=1 uv run --extra typecheck mypy api core --pretty # Python changes PYTHON_DOTENV_DISABLED=1 uv run pytest tests/unit tests/integration -n auto # or the narrower set the issue names - PYTHON_DOTENV_DISABLED=1 uv run python -m tools.changelog check --base origin/main + PYTHON_DOTENV_DISABLED=1 uv run python -m tools.changelog check --base origin/main # after committing the fragment cd app && yarn lint && yarn fm:check && yarn type-check && yarn test && yarn build # app/ changes ``` + The changelog check reads `origin/main...HEAD`, as CI does, so it fails + until the fragment is committed; staging it is not enough. + 6. **Commit each finished step** on your branch with a conventional message. Its body names the step (for example `Step 2 of 5: …`) and ends with `Refs #`, followed by a co-author trailer crediting yourself, the @@ -179,20 +208,38 @@ This section is for the Gemini agent in the owner's agy session. 7. **Hand over** when you finish or stop, whether on a blocker or at the end of the session. Push your branch and open a draft PR, or update the one you - opened before. Write the body into a temp file first. It carries - `Closes #` and lists: - - what changed and why, - - the commands you ran and their results, - - the decisions you made, - - the steps still open, - - your questions. + opened before. Write the body into a temp file first, in the repository's + PR format, so Claude can ship it without rewriting it: + + ```markdown + Closes # + + ## Summary + - What changed and why, one bullet per change; name every file outside + the issue's Constraints and the gate that made you touch it. + + ## Plan + Issue #. + + ## Test plan + - [x] Each command you ran, with its result line. + + ## Decisions + - The choices the issue left to you, and what you chose. + + ## Open steps and questions + - The steps still open and your questions, or "None". + ``` ```bash git push -u origin gemini/- gh pr create --draft --label gemini --title ": " --body-file # first hand-over - gh pr edit --body-file # later ones + gh api -X PATCH repos/{owner}/{repo}/pulls/ -F body=@ # later ones ``` + `gh pr edit` fails on this repository (Projects classic), so a later + hand-over updates the body through `gh api`. + Then tell the owner the PR number, and stop. **Never** do any of these: @@ -213,7 +260,10 @@ This section is for the Gemini agent in the owner's agy session. 1. **Find the drafts.** When the owner says Gemini is done, or at the start of a session, list them with `gh pr list --draft --label gemini`. Read the PR body (the hand-over) and the CI results on the draft. Open steps or questions go - back to the owner first. + back to the owner first. Skills load from the shared checkout, so when its + `main` is behind `origin/main` and clean, fast-forward it + (`git pull --ff-only origin main`) before you follow these steps; on + 2026-10-22 a stale checkout still described the take-over from the clone. 2. **Check it out** into a fresh worktree of your own checkout: ```bash @@ -221,6 +271,9 @@ This section is for the Gemini agent in the owner's agy session. git worktree add .claude/worktrees/gemini- gemini/- ``` + A new worktree has no environment; sync it with the extras from section 2, + step 2, before you run anything in it. + When Gemini hands over again after step 4, the worktree exists and Git refuses to fetch into its checked-out branch, so bring in its new commits there instead: @@ -239,10 +292,12 @@ This section is for the Gemini agent in the owner's agy session. continue. Bring in a newer `origin/main` by merging it into the branch, never by rebasing a pushed branch. 5. **Ship.** Run the open-pr gates, add the PR number to the changelog - fragment, and add one Summary bullet to the PR body: the change was - implemented by Gemini 4 Argon in the owner's interactive agy session and - reviewed by Claude. This is provenance for the reviewer, not a "Generated - with" footer. Then `gh pr ready ` starts the Copilot review; follow the + fragment, and add one Summary bullet to the PR body, which Gemini wrote in + the repository's format (section 2, step 7): the change was implemented by + Gemini 4 Argon in the owner's interactive agy session and reviewed by + Claude, with the defects the review found and the commits that fixed them. + This is provenance for the reviewer, not a "Generated with" footer. Then + `gh pr ready ` starts the Copilot review; follow the PR through as `/open-pr` describes. Merge only on the owner's explicit OK in the session. 6. **Clean up.** After the merge, remove your worktree. Deleting a local diff --git a/.claude/skills/verify-core/SKILL.md b/.claude/skills/verify-core/SKILL.md index bb321858fc6..07c98493ccb 100644 --- a/.claude/skills/verify-core/SKILL.md +++ b/.claude/skills/verify-core/SKILL.md @@ -89,9 +89,12 @@ To check against **real** data, don't reach for the DB from a script (PostgreSQL in prod, SQLite in tests). When adding a column type, verify it on both: an integration test (SQLite) plus one e2e run or a careful review of the PostgreSQL branch. -- **Plain `uv run ruff` can fail on a fresh venv** — dev tools live - in extras; `uv sync --extra dev` (or `--all-extras`) first, and - mypy always needs `--extra typecheck`. +- **A fresh venv (a new worktree or clone) lacks the extras** — dev + tools live in extras, and the tests under `tests/unit/agents/` fail + at collection without `google-adk`. Sync once with what CI and the + gates use: `uv sync --extra test --extra dev --extra typecheck --extra agents` + (CI's "Run Tests" syncs `--extra test --extra agents`); mypy always + needs `--extra typecheck`. ## Troubleshooting diff --git a/changelog.d/gemini-task-friction.md b/changelog.d/gemini-task-friction.md new file mode 100644 index 00000000000..4535138e1e2 --- /dev/null +++ b/changelog.d/gemini-task-friction.md @@ -0,0 +1,9 @@ +### Changed + +- **Gemini tasks after the first pilot.** The `gemini-task` skill now reads + issues with `gh issue view --json` and updates PR bodies through `gh api`, + because both bare commands fail on this repository; asks issue authors for + cases where the existing rules must still win and for the files the gates + couple; names the environment sync with the `agents` extra; and has Gemini + hand over in the repository's PR format. The changelog check names a + fragment that is on disk but not yet committed. diff --git a/tests/unit/tools/test_changelog_tool.py b/tests/unit/tools/test_changelog_tool.py index 08103ae895f..f2e56dace0f 100644 --- a/tests/unit/tools/test_changelog_tool.py +++ b/tests/unit/tools/test_changelog_tool.py @@ -275,6 +275,16 @@ def test_a_pr_without_a_fragment_fails_and_names_the_way_out(repo: Path) -> None assert cl.SKIP_LABEL in problem +def test_an_uncommitted_fragment_is_named_in_the_failure(repo: Path) -> None: + """The gate reads commits, as CI does; a fragment still on disk only gets a pointer, not a pass.""" + (repo / "core.py").write_text("x = 2\n", encoding="utf-8") + _commit_all(repo, "change") + (repo / "changelog.d" / "topic.md").write_text(FRAGMENT, encoding="utf-8") + (problem,) = cl.check_pr("main", root=repo) + assert "no changelog fragment" in problem + assert "changelog.d/topic.md is not committed yet" in problem + + def test_a_catalogue_only_pr_needs_no_fragment(repo: Path) -> None: """What the automated plot pipeline produces, and what a release summarizes in one italic *Catalog* line rather than per PR.""" diff --git a/tools/changelog/__init__.py b/tools/changelog/__init__.py index f78bd965eca..e1382ed87dc 100644 --- a/tools/changelog/__init__.py +++ b/tools/changelog/__init__.py @@ -458,9 +458,18 @@ def check_pr(base: str, *, root: Path = REPO_ROOT) -> list[str]: touches_fragments = any(p.startswith(f"{FRAGMENT_DIR_NAME}/") for p in changed) exempt = bool(changed) and all(p.startswith(EXEMPT_PREFIXES) for p in changed) if changed and not (touches_fragments or release_cut or exempt): - problems.insert( - 0, + problem = ( f"no changelog fragment: add {FRAGMENT_DIR_NAME}/.md (format: {FRAGMENT_DIR_NAME}/README.md), " - f"or label the PR '{SKIP_LABEL}' if it truly changes nothing worth a line", + f"or label the PR '{SKIP_LABEL}' if it truly changes nothing worth a line" ) + if pending := _uncommitted_fragments(root): + problem += f"; {', '.join(pending)} is not committed yet, and the check reads commits only" + problems.insert(0, problem) return problems + + +def _uncommitted_fragments(root: Path) -> list[str]: + """Fragments in the working tree that no commit holds yet: the gate, like CI, reads only `base...HEAD`.""" + out = _git(root, "status", "--porcelain", "--untracked-files=all", "--", FRAGMENT_DIR_NAME) + paths = [line[3:] for line in out.splitlines()] + return [p for p in paths if p.endswith(".md") and Path(p).name != "README.md"] From 70b2f4b1adb3b0d07018d8dec7f1678f9542b344 Mon Sep 17 00:00:00 2001 From: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com> Date: Thu, 22 Oct 2026 16:48:07 +0200 Subject: [PATCH 2/2] docs(changelog): add the PR number to the gemini-task fragment Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M --- changelog.d/gemini-task-friction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/gemini-task-friction.md b/changelog.d/gemini-task-friction.md index 4535138e1e2..27d322fe5bd 100644 --- a/changelog.d/gemini-task-friction.md +++ b/changelog.d/gemini-task-friction.md @@ -6,4 +6,4 @@ cases where the existing rules must still win and for the files the gates couple; names the environment sync with the `agents` extra; and has Gemini hand over in the repository's PR format. The changelog check names a - fragment that is on disk but not yet committed. + fragment that is on disk but not yet committed. (#12229)