From 40a697cdf9183268af37895172a019ac40e5270a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 2 Nov 2026 18:58:29 +0000 Subject: [PATCH] chore: update Copilot collections to v0.21.0 --- .github/.copilot-collections.yaml | 2 +- .github/skills/update-starbase/SKILL.md | 248 +++++++++++++++++ .../references/file_ownership.md | 253 ++++++++++++++++++ .../references/manual_merge_steps.md | 65 +++++ .../references/pr_provenance.md | 92 +++++++ .../references/preparation_prs.md | 125 +++++++++ .../references/refresh_stale_pr.md | 69 +++++ .../references/reviewer_feedback_conflicts.md | 154 +++++++++++ .../update-starbase/scripts/prepare_merge.sh | 198 ++++++++++++++ 9 files changed, 1205 insertions(+), 1 deletion(-) create mode 100644 .github/skills/update-starbase/SKILL.md create mode 100644 .github/skills/update-starbase/references/file_ownership.md create mode 100644 .github/skills/update-starbase/references/manual_merge_steps.md create mode 100644 .github/skills/update-starbase/references/pr_provenance.md create mode 100644 .github/skills/update-starbase/references/preparation_prs.md create mode 100644 .github/skills/update-starbase/references/refresh_stale_pr.md create mode 100644 .github/skills/update-starbase/references/reviewer_feedback_conflicts.md create mode 100755 .github/skills/update-starbase/scripts/prepare_merge.sh diff --git a/.github/.copilot-collections.yaml b/.github/.copilot-collections.yaml index 4bb564c7f..40361c2ce 100644 --- a/.github/.copilot-collections.yaml +++ b/.github/.copilot-collections.yaml @@ -1,7 +1,7 @@ # https://github.com/canonical/copilot-collections copilot: - version: v0.18.0 + version: v0.21.0 collections: - starcraft-core - starcraft-docs diff --git a/.github/skills/update-starbase/SKILL.md b/.github/skills/update-starbase/SKILL.md new file mode 100644 index 000000000..6eac15373 --- /dev/null +++ b/.github/skills/update-starbase/SKILL.md @@ -0,0 +1,248 @@ +--- +name: update-starbase +description: Syncs Starbase-managed project files by adding the canonical/starbase remote and merging starbase/main into the current branch. Use when updating common.mk and related shared Starcraft build/CI conventions. +--- + +# Update Starbase + +## Phase 1: Merge Starbase + +Run the preparation script from the root of the child repository: + +```bash +bash /scripts/prepare_merge.sh [BRANCH_SUFFIX] +``` + +- `BRANCH_SUFFIX` is optional; defaults to today's date + (`work/update-starbase-YYYY-MM-DD`). +- The script handles steps 1–6: safe-state check, remote setup, fetch, + work-branch creation, merge, and conflict detection. +- It prints a compact `STATUS=...` block (`BRANCH`, `BASE`, `CHANGED_FILES`, + and — when relevant — `CONFLICTED_FILES` and `PLACEHOLDER_WARNINGS`) for + one of four `STATUS` values, then continue with the matching step below: + - `STATUS=clean` (exit 0): merge succeeded with no conflicts. Continue to + Phase 2. + - `STATUS=conflicted` (exit non-zero): merge stopped with conflicts. + Resolve each file in `CONFLICTED_FILES` per + [`references/file_ownership.md`](references/file_ownership.md), then + `git add` the resolved files and `git merge --continue` before + continuing to Phase 2. + - `STATUS=already-up-to-date` (exit 0): `starbase/main` has no commits + that aren't already in the branch. No work branch was created — there + is nothing to sync or open a PR for. Stop here and report this to the + user. + - `STATUS=failed` (exit non-zero): `git merge` failed for a reason other + than an ordinary conflict (e.g. untracked files that would be + overwritten, a failing merge hook). The merge was aborted; fix the + underlying issue from the git output above the status block, then + re-run the script. + +If the script cannot be run, follow [`references/manual_merge_steps.md`](references/manual_merge_steps.md) instead. + +Report one of: `merge-clean`, `merge-conflicted` (with the conflicted file +list), `already-up-to-date`, or `merge-failed`. + + +### Conflict resolution + +When resolving merge conflicts, consult +[`references/file_ownership.md`](references/file_ownership.md) for the +complete ownership map and decision rules, including: + +- **Conflict rule 1** – File ownership map (always take starbase, always take + child, and mixed-ownership rules by file/section). +- **Conflict rule 1b** – Files that must be deleted; check every merge, + clean or conflicted, since these deletions never raise a conflict. +- **Conflict rule 2** – Python type annotation modernisation (when to apply + `X | Y`, `tuple[X, Y]`, etc., and Python version caveats). +- **Conflict rule 3** – AGENTS template handling (library vs. application + repositories). + +If a reviewer's PR feedback asks for something that contradicts one of these +rules (or any other rule in this skill), see +[`references/reviewer_feedback_conflicts.md`](references/reviewer_feedback_conflicts.md) +for how to resolve it — don't silently follow one side without recording why. + +### Merge commit message format + +When finalizing the Starbase merge commit, include the merge date as an +ISO 8601 date only (`YYYY-MM-DD`) in the commit message. + +Do not include time or timezone. + +Example: +- `chore(merge): update starbase (2026-06-18)` + +The commit body must include a concise overview of the Starbase changes that +were pulled in (for example: build-system updates, workflow changes, docs +tooling changes, or dependency-management adjustments). + +Use this template: + +```text +chore(merge): update starbase () + +Merge `starbase/main` into this branch and sync Starbase-managed updates. + +Overview of Starbase changes pulled in: +- +- +- +- + +Conflict resolution applied: +- Kept child-repo version of `` (`--ours`). +- Kept child-repo version of `` (`--ours`). +- Took full `` from `starbase/main` (source-of-truth file). +- Took full `` from `starbase/main` (source-of-truth file). +- For this `` repo: + - kept `` deleted, + - kept `` deleted, + - kept `AGENTS.md` authoritative and applied relevant updates from ``. +``` + +## Phase 2: Validation & Cleanup + +1. Run the deletion checklist (required, every merge): + Before validation, work through + [`references/file_ownership.md`](references/file_ownership.md)'s + "Conflict rule 1b" — it doesn't show up as a conflict, so this is the + only point it gets caught. +2. Verify mixed-ownership files and clean up placeholders: + - **Structural diff**: For any modified mixed-ownership file, diff against Starbase (`git diff starbase/main -- `) and verify only child-owned values differ per [`references/file_ownership.md`](references/file_ownership.md). + - **Placeholder grep**: Scan for residual `starcraft|starbase` placeholder text: + + ```bash + git diff --name-only --diff-filter=d HEAD^1 HEAD | xargs -r grep -i -E "starcraft|starbase" + ``` + + Update any matches found (except external docs/style guide URLs) to use the child repository's name/purpose. Do **not** touch legitimate Starbase-ownership comments. Explicitly disposition every hit (fix or note as legitimate attribution in the merge commit message). + +3. Run pre-PR validation (required): + + Before creating a PR, ensure repository checks pass: + + ```bash + make format + make lint + make docs + make test-fast + ``` + + `make lint` builds the docs as part of linting, but run `make docs` too so + docs-specific failures are caught and reported clearly on their own, not + just as a side effect of the lint target. + + `make format` can rewrite doc-referenced code snippets (e.g. quote style) + pulled in via `include`/`literalinclude` (or equivalent) directives + without failing `make docs`. After formatting, verify each such directive + still resolves to the in-sync excerpt the docs page expects, adjusting + anchors/line numbers if formatting shifted them. + + If Vale or `make docs` reports spelling failures: + - **`Starbase` or `Starcraft`**: Indicates unmigrated template prose in `docs/`. Rewrite the prose for the child repository; never add these to `.custom_wordlist.txt`. + - **One-off proper nouns**: If a flagged term is clearly a proper noun used only once or twice in the documentation, apply a scoped inline ignore (`vale-ignore`) at the point of use instead of adding it to the global wordlist. + - **Other terms**: Do not unilaterally edit `docs/.custom_wordlist.txt` to force a clean build. If in an interactive session, ask the operator how to disposition the flagged words (add to wordlist, inline ignore, or rewrite); only fall back to listing unresolved words in the PR description if in a non-interactive session. + + Do not create the PR until these commands complete successfully. + +4. Check every file changed by the merge for preparation PR candidates + (required): + + Some fixes discovered while merging or validating are pre-existing or + unrelated issues (e.g. latent bugs, mechanical formatting diffs, new + default-enabled tooling, or large net-new template files). Bundling these + into the merge PR obscures the merge diff. This is a mandatory audit, not + a reactive check — do not wait for a reviewer to flag it. Lean toward + distilling changes that could reasonably land on `main` independently + (a new formatter's reformatting pass is the canonical example), without + over-splitting small incidental fixes. + + When you find one, split it into a separate draft preparation PR against + `origin/main` before finalizing the merge — using its own git worktree, + and optionally delegated to a background subagent, so it doesn't block + the main merge work. Consult + [`references/preparation_prs.md`](references/preparation_prs.md) for + trigger criteria, the worktree/subagent workflow, stacked PR workflows, + lifecycle requirements (draft-only, operator promotion), and provenance + link rules. + +## Phase 3: Pull Request Creation + +When opening the PR, apply the label: +- `PR: Merge` + +Document change provenance on each file: +For every file added, deleted, or modified by the merge, post GitHub PR +review comments explaining the provenance of the changes. Consult +[`references/pr_provenance.md`](references/pr_provenance.md) for the +comment format, prefix template, provenance-description conventions, the +GitHub API details (rate limiting, `subject_type: file`), and the rules +for suggestion-only own-invention workarounds and precedent citations. + +Open the PR as a draft, request a Copilot review while it is still draft +(`gh pr create --draft --reviewer "copilot-pull-request-reviewer[bot]" ...`, +or `gh pr edit --add-reviewer +"copilot-pull-request-reviewer[bot]"` on an already-open PR), and keep +iterating until the review is clean enough for the operator to mark it +ready. + +## Phase 4: Review, Squashing & Promotion + +- Check the PR's CI status before handing it off. +- If a reviewer's requested change contradicts a rule in this skill (the + file-ownership map, a conflict-resolution rule, or any other documented + rule), see + [`references/reviewer_feedback_conflicts.md`](references/reviewer_feedback_conflicts.md) + before acting on it. +- If CI is still running or likely to fail, start a background agent to watch + the PR checks and report failures so they can be fixed promptly. +- You do not need to wait for the full matrix to finish before fixing failures + in jobs that are already failing or have enough signal to act on. +- While the PR is still in draft (fixing initial CI failures and addressing + the first round of review comments), make separate commits for each + follow-up fix rather than amending; this keeps the history of what changed + and why easy to review incrementally. +- If significant time has passed since the merge PR was opened and either + `origin/main` or `starbase/main` has gained new commits (including + "GitHub reports conflicts with `main`"), do not layer another merge commit + on top of the existing one. Follow + [`references/refresh_stale_pr.md`](references/refresh_stale_pr.md) to + rebuild the merge commit and preserve all existing follow-up commits. +- Squash the follow-up-fix commits back into the single merge commit twice, + at two distinct points: + 1. Once CI is green and the PR is ready to come out of draft: squash all + follow-up-fix commits made so far into the original merge commit, then + send the operator a message with the draft PR URL and a brief summary + (what was merged, any conflicts resolved, any custom changes made) and + stop. **The agent must never mark any PR ready for review itself** — + regardless of harness type (interactive or non-interactive) — only the + operator takes a PR out of draft. Leave promoting the PR to the + operator. + 2. Right before the final merge into the base branch: squash any further + follow-up commits made during the ready-for-review round (e.g., fixes + from human reviewers) back into that same single merge commit. + Between these two squash points, follow-up fixes should again land as + separate commits, not be squashed continuously. + Use this non-destructive recipe for each squash (a plain `git rebase -i` + can drop the merge commit's second parent or flatten the history, since + the branch's first commit is itself a merge commit): + ```bash + # Find the merge commit created by the sync (its only merge commit). + MERGE_HASH=$(git log --merges -n 1 --format=%H) + + # Soft-reset to it: this keeps every follow-up-fix diff staged, without + # touching the merge commit's parents. + git reset --soft "$MERGE_HASH" + + # Fold the staged diffs into the merge commit, keeping its message/parents. + git commit --amend --no-edit + + # Push the rewritten branch. + git push --force-with-lease origin + ``` +- **The provenance/documentation rules for follow-up fixes are documented in + [`references/pr_provenance.md`](references/pr_provenance.md)** — they + apply for the entire life of the PR, not just the initial merge, and + every follow-up-fix review comment must be posted in the same turn you + push the corresponding fix. diff --git a/.github/skills/update-starbase/references/file_ownership.md b/.github/skills/update-starbase/references/file_ownership.md new file mode 100644 index 000000000..9ae613395 --- /dev/null +++ b/.github/skills/update-starbase/references/file_ownership.md @@ -0,0 +1,253 @@ +# File Ownership Map and Conflict Resolution Rules + +Use this reference when resolving conflicts during a Starbase sync merge. + +## Conflict rule 1: File ownership map + +### Always take `starbase/main` + +- Files marked in-tree as Starbase source-of-truth files (for example, + comments like "Should only be edited in the `starbase` repository"). +- `.editorconfig`: the shared baseline is Starbase-owned; the child repository + may append additional sections, but must not override the shared defaults. +- `common.mk` (explicitly marked as Starbase-owned in-tree). +- `.github/workflows/check-renovate.yaml`. +- `.github/workflows/release-publish.yaml`. +- `.github/workflows/tics.yaml`: keep the reusable workflow call, but set + `with.project` to the child repository name; do not leave the project + commented out in the shipped PR. +- `.github/workflows/security-scan.yaml`: keep the osv-scanner config pointing + at the root `osv-scanner.toml`, not a nested `source/` path. +- `.gitignore`: the shared baseline is Starbase-owned; the child repository may + append repository-specific ignore entries at the bottom. **Don't silently + remove child entries that look redundant with the shared baseline** — a + reviewer may know of a real (if undocumented) reason a repo-specific entry + exists (e.g. files generated only by running that repo's own scripts). If + an entry looks removable, keep it and add a short comment marking it as a + repository-specific addition instead of deleting it; only remove an entry + outright when you have positive confirmation it's obsolete (e.g. `git + blame`/history showing it's unused, or explicit reviewer confirmation). +- `.pre-commit-config.yaml` for the shared hook set; if the same hook appears + in both repos, keep the newer revision pin. **Never downgrade a hook**: for + every hook present in both the child repo's current config and Starbase's, + explicitly compare the `rev` (or version) pins before merging — don't + assume Starbase's value is always newer just because it's the sync source. + If Starbase's pin for a hook is actually older than the child's current + pin, keep the child's newer pin for that hook and note the exception in the + merge commit message (see "Conflict resolution applied" in the merge commit + message template); this is a case where the "always take starbase/main" + default is overridden by a concrete version comparison, not by reviewer + preference. As part of pre-PR validation, diff the resulting + `.pre-commit-config.yaml` against both source files' hook lists to confirm + no hook's version regressed. +- `.readthedocs.yaml`. +- `README.md`. +- Any other shared build or workflow file that is explicitly documented as + Starbase-managed in this skill or the repository docs. + +### Always take the child repository version + +- `.github/CODEOWNERS` +- `.github/workflows/qa.yaml` +- `uv.lock` + +### Mixed ownership + +In mixed-ownership files, resolve per declaration rather than per section. Starbase owns structure and syntax (including upstream deletions and commented-out scaffolding); the child repository only provides values. Verify with: + +```bash +git diff starbase/main -- +``` + +Check deletions explicitly: when Starbase removes a declaration, `git merge` raises no conflict marker and placeholder greps cannot detect the omission, leaving stale lines in place. Every surviving difference must map to an allowed child-owned value below. + +- `.github/.jira_sync_config.yaml`: keep `settings.components` and + `settings.jira_project_key` from the child repository; take the rest from + `starbase/main`. +- `.github/PULL_REQUEST_TEMPLATE.md`: keep Starbase's template content and + replace only the contribution-guidelines link with the child repository's + `CONTRIBUTING.md` URL. +- `SECURITY.md`: keep the Starbase comment under `Release cycle`, keep the + child repository's release-cycle wording, and keep the project-specific + reporting links from the child repository. +- `docs/.sphinx/` → `docs/_dev/` (or any similar Starbase docs-tooling + directory rename/consolidation): take the new directory from + `starbase/main`, then delete the old directory outright — do not leave it + in place alongside the new one. Search the repo for remaining references + to the old path (`Makefile`, `docs/conf.py`, `.readthedocs.yaml`, CI + workflows, `tox.ini`/`noxfile.py`, etc.) and update or remove each one; a + clean `grep` for the old directory name across the repo should return + nothing once the migration is finished. +- `docs/**/*.rst`: keep the repository's own documentation pages, including the + files inside the diataxis directories. Update any copied Starbase text to the + child repository's name and purpose; if the landing page is being published, + make sure it is not excluded from the Sphinx build. +- If the empty-diataxis landing pages are still placeholders, keep them excluded + from `docs/conf.py`; only un-exclude them when the content is ready to ship. +- `docs/{how-to,explanation,reference,tutorials}`: the directory names are + Starbase-owned; if they move, move the whole docs tree accordingly. +- `docs/.custom_wordlist.txt`: keep child repository entries and include new + shared tooling terms from `starbase/main`. Never add `Starbase` or `Starcraft` + to a child wordlist (rewrite the prose instead). For isolated proper nouns, + use scoped inline ignores (`vale-ignore`); for other misspellings, ask the + operator (falling back to the PR description only if non-interactive) rather + than editing this file unilaterally during validation. If it is unclear + whether this file should be reclassified as fully Starbase-owned going forward, + raise that as an explicit question for the reviewer/maintainer rather than + deciding unilaterally. +- Whenever a Starbase-driven rename or move deletes a documentation file or + directory that existed before the merge (for example + `docs/how-to-guides/` → `docs/how-to/`), add a matching entry to + `docs/redirects.txt` (using the repository's existing redirect mechanism, + e.g. `sphinx-rerediraffe`) so old links keep resolving. Confirm with + `make docs` that the build reports `(good) --> ` for + each added redirect. **A directory-level redirect (with + `rediraffe_dir_only`) only redirects that directory's own index page, not + the individual files that used to live inside it** — add one explicit + redirect entry per moved file as well (e.g. both + `"how-to-guides" "how-to"` and `"how-to-guides/add_repo" "how-to/add_repo"`) + and confirm each one individually reports `(good) ...` in the `make docs` + output; do not assume the directory entry alone covers its contents. +- When a moved/renamed file lands via delete+add rather than a clean git + rename (so the diff doesn't show the content as untouched), diff the old + and new file contents directly and compare the new file against sibling + pages that already follow the child repository's conventions (for example, + other `docs/*/index.rst` files). Reconcile any stale conventions the move + carried over (e.g. an old `toctree` option like `:maxdepth:` where sibling + pages now use `:hidden:`) rather than assuming the moved file is + already up to date. +- File-deletion rules for library repositories (`docs/release-notes/`, + `.github/README.md`, `starcraft/__init__.py`, AGENTS templates) live in + "Conflict rule 1b: Files that must be deleted" below. +- For library repositories, replace any scaffolded `CONTRIBUTING.md` with a + short repository-specific guide that matches the actual project name, repo + URLs, and commands. +- `pyproject.toml`: + - Keep the entire `[project]` block (including `[project.scripts]` and the + `license` key) from the child repository. + - Keep all `[dependency-groups]` entries from the child repository. + - The `docs-sphinx-stack` dependency group (and its list of + active/commented-out packages) is fully owned by `starbase/main`; take it + verbatim, including which entries are commented out. Do not add, remove, or + uncomment entries in this group to suit the child repository — any + child-specific docs extensions belong in the `docs` group instead, + alongside the `{ include-group = "docs-sphinx-stack" }` reference. + If a conflict arises here, prefer starbase's content and regenerate + `uv.lock`. + - Merge `tool.uv.constraint-dependencies` by keeping the higher version from + each side, ordered alphabetically by package name. + - Keep `[build-system]` from the child repository. + - Keep `[tool.setuptools_scm]` from `starbase/main`. + - Treat `tool.pytest.ini_options.markers` as shared; update the + Starbase-defined markers and append any child-specific markers as needed. + - Keep `[tool.pyright]` from the child repository. + - Keep `[tool.mypy]` from the child repository. + - Any Starbase-side reference to the `starcraft` module is child-owned; keep + the child repository's module names instead. + - Keep `tool.ruff` from `starbase/main`, except for `tool.ruff.src` and + `tool.ruff.target-version`; the child repository may add values to + `tool.ruff.extend-exclude`, `tool.ruff.lint.select`, + `tool.ruff.lint.ignore`, and `tool.ruff.lint.per-file-ignores`. +- `.github/workflows/policy.yaml`: take the file's structure from + `starbase/main` — trigger conditions, job names, `permissions` blocks, and + the reusable-workflow `uses:` refs. The child repository owns the `with:` + input values passed to those reusable workflows (e.g. `osv-exclude-paths`, + `uv-export-no-groups`, and any other repo-specific scan configuration): + preserve the child's values entry-by-entry when merging Starbase's + structural changes, and keep the child's entries if Starbase's scaffold + omits them. If Starbase adds a *new* `with:` key that didn't exist on + either side before, take Starbase's default for that key and flag it in + the merge commit message so the reviewer can tune it. +- `osv-scanner.toml`: the child repository owns this file's contents. + Starbase ships only a skeleton header (`# OSV Scanner configuration`), so + a merge must never replace the child's `[[IgnoredVulns]]` entries with + that skeleton. If the file already exists in the child repository, keep it + as-is (aside from adopting a new header line if Starbase introduces one). + If the merge deletes it (or it never existed), recreate it containing only + Starbase's current header line, with no entries. +- `Makefile`: keep child-owned variables such as `PROJECT`, but include new + Starbase-added variables and take the Starbase version when it extends an + existing variable. Use it to set the child repo's docs venv default so docs + install into a separate environment from the main `uv` project venv. +- `docs/conf.py`: keep the Starbase docs scaffold; substitute child-owned values: + - **Starbase-owned**: `version`/`release` logic (including commented-out blocks and Read the Docs branch logic), `copyright` format, `html_title` (remove if deleted upstream), `html_favicon`, `html_extra_path`, and new upstream `exclude_patterns` entries. + - **Child-owned values**: `project`, `author`, `ogp_*`, `html_context` URLs, `llms_txt_description`, license name, and quadrant exclusion toggles in `exclude_patterns`. + - **Copyright start year**: derive from first commit (`git log --reverse --format=%ad --date=format:%Y | head -1`); if history is shallow, squashed, or the year looks wrong, ask the reviewer. + - **`llms_txt_description`**: derive strictly from the opening sentence of `docs/index.rst` using `"This is the documentation for " + ` (e.g. `**Imagecraft** is a tool...` becomes `This is the documentation for Imagecraft, a tool...`). Do not invent original prose. +- `.readthedocs.yaml`: keep the Read the Docs build using a separate docs + virtualenv instead of pointing both the docs venv and the uv project env at + the same path. +- Branch names for this workflow should start with `work/`. +- `tests/`: keep the child repository versions for everything under `tests/`. + Do not add `tests/integration/test_setuptools.py` from `starbase/main`; it is + a Starbase-scaffold-only test and is not needed in child repositories, even + though shared fixtures it relies on (e.g. `project_main_module`) may + legitimately live in the child's `conftest.py` for other tests. + +## Conflict rule 1b: Files that must be deleted + +These deletions never raise a git conflict, so check this list explicitly on +every merge — clean or conflicted — even if the files never appear in the +diff: + +- For library repositories, delete `docs/release-notes/`, + `.github/README.md`, and the Starbase scaffold package + `starcraft/__init__.py`. +- `AGENTS.lib.md`/`AGENTS.app.md`: delete per repository type, per + "Conflict rule 3: AGENTS templates" below. +- Any old directory superseded by a Starbase reorg (e.g. `docs/.sphinx/` → + `docs/_dev/`, see "Mixed ownership" above): delete the old directory and + its dangling references. + +## Conflict rule 2: Type annotations + +After resolving conflicts, check for Python type annotation updates needed: +- If Starbase imports change (e.g., removing `Tuple`, `Union` from typing imports), + update the child repository's code to use modern Python 3.10+ syntax: + - Replace `Tuple[X, Y]` with `tuple[X, Y]` + - Replace `Union[X, Y]` with `X | Y` + - Replace `Dict[K, V]` with `dict[K, V]` + - Replace `List[X]` with `list[X]` +- The `X | Y` union syntax requires `from __future__ import annotations` in any + module that must run on Python 3.9 (it is available natively at runtime from + Python 3.10+). Verify the child project's minimum Python version before + relying on it at runtime. +- Run `ruff check --fix` and `ruff format` to auto-fix these issues. + +## Conflict rule 3: AGENTS templates + +For repository-specific AGENTS templates: +- keep `AGENTS.md` as the authoritative file, +- keep the template file for the repository type deleted after using it only as + a reference, +- apply only relevant improvements from the template into `AGENTS.md`. +- when the scaffold ships `AGENTS.md` from Starbase, replace it with the + repository-specific version derived from the matching template (`AGENTS.lib.md` + for libraries or `AGENTS.app.md` for applications), then delete both template + files. + +For library repositories: +- use `AGENTS.lib.md` as the source template for `AGENTS.md`, +- keep `AGENTS.app.md` deleted, +- keep `AGENTS.lib.md` deleted after using it only as a template reference. + +For application repositories: +- use `AGENTS.app.md` as the source template for `AGENTS.md`, +- keep `AGENTS.lib.md` deleted, +- keep `AGENTS.app.md` deleted after using it only as a template reference. + +Preserving template structure while filling it in: +- Preserve the source template's structure and formatting (tables, headings, + list types) when filling in TODOs and repo-specific details. Only change + formatting if the content genuinely cannot fit in the existing structure + (e.g. a table column is too narrow for a required value), and if so, explain + why in the merge commit message. +- When the current repository's own package must be removed from a list of + dependencies (e.g. a self-reference in a "Craft apps and libraries" table), + do this as a targeted row/item removal — not as a side effect of + reformatting the surrounding list or table into a different structure. +- Before finalizing `AGENTS.md`, diff its structure against the source + template (headings, tables vs. prose, list types) to confirm only intended + repo-specific substitutions and deletions were made. Flag any unintended + structural drift (e.g. a table collapsed into a flat list) for review rather + than merging it silently. diff --git a/.github/skills/update-starbase/references/manual_merge_steps.md b/.github/skills/update-starbase/references/manual_merge_steps.md new file mode 100644 index 000000000..4e9e67a9b --- /dev/null +++ b/.github/skills/update-starbase/references/manual_merge_steps.md @@ -0,0 +1,65 @@ +# Manual Merge Steps (fallback) + +Use these steps if `scripts/prepare_merge.sh` cannot be run directly +(e.g. no shell access, restricted environment, or debugging a script failure). +They are the exact equivalent of what the script does. + +1. Confirm the repository is in a safe state before merge: + + ```bash + git --no-pager status --short --branch + ``` + + If there are unrelated local edits, stop and decide whether to stash or + commit first. + +2. Ensure a `starbase` remote exists and points to Canonical Starbase: + + ```bash + git remote get-url starbase >/dev/null 2>&1 \ + && git remote set-url starbase https://github.com/canonical/starbase.git \ + || git remote add starbase https://github.com/canonical/starbase.git + ``` + +3. Fetch the Starbase refs: + + ```bash + git fetch starbase --prune + ``` + +4. Check whether there's anything new to sync — stop here (without creating + a work branch) if `starbase/main` has no commits that aren't already in + `HEAD`: + + ```bash + git merge-base --is-ancestor starbase/main HEAD && echo "Already up to date with starbase/main" + ``` + + If that prints the message, there is nothing to merge and no PR is + needed; stop before creating a branch. + +5. Create a work branch before starting the merge (match `prepare_merge.sh`'s + naming so both paths are consistent: `work/update-starbase-YYYY-MM-DD`, + or append a custom suffix instead of the date): + + ```bash + git switch -c work/update-starbase-YYYY-MM-DD + ``` + +6. Merge from Starbase main into the current branch (`--allow-unrelated-histories` + is required for a repository's first-ever Starbase sync, and is a safe + no-op on repositories that already share history with Starbase): + + ```bash + git merge --no-ff --allow-unrelated-histories starbase/main + ``` + +7. Capture merge state: + - If merge succeeds, continue to project checks. + - If merge conflicts, stop and report conflicted files: + + ```bash + git --no-pager status --short + ``` + + Then consult [`file_ownership.md`](file_ownership.md) to resolve each conflict. diff --git a/.github/skills/update-starbase/references/pr_provenance.md b/.github/skills/update-starbase/references/pr_provenance.md new file mode 100644 index 000000000..f8b90ffd4 --- /dev/null +++ b/.github/skills/update-starbase/references/pr_provenance.md @@ -0,0 +1,92 @@ +# PR Provenance and Review Comments + +Use this reference whenever you need to document the provenance of a change +on the merge PR — both for the initial merge and for every follow-up fix made +while the PR is open. + +## Documenting change provenance on each file + +For every file added, deleted, or modified by the merge, make review comments +on the GitHub PR explaining the provenance of the changes. Additionally, post +inline review comments pointing out specific custom changes (e.g., removing a +duplicate directive or fixing a type ignore). + +Files that already existed in the child repository and merged cleanly +without conflicts or custom changes do not need a comment. + +**Provenance and rationale for manual changes belong exclusively in PR review +comments, never as comments inside the source file.** If you catch yourself +writing "why a bot changed this" as a `#`/`//` comment in code, stop and move +it to a PR review comment instead. This applies just as much to config files +like `docs/conf.py`: e.g. explaining *why* only specific `sphinx_toolbox` +submodules are loaded (instead of the top-level package) belongs in a PR +file-level comment, not a multi-line `#` block above the `extensions` entries. + +The comments must follow these guidelines: + +- **Prefix Template**: Each comment must begin with a robot emoji and a + prefix in square brackets announcing that a bot wrote it, along with the + model and harness. E.g. `🤖 [BEEP BOOP, A BOT WROTE THIS COMMENT - , ]`. + Example: `🤖 [BEEP BOOP, A BOT WROTE THIS COMMENT - Gemini 3.5 Flash (High), antigravity]` +- **Provenance Descriptions**: + - For new files: `"new file from starbase"` + - For modified files: `"file updated from starbase"` + - For renamed or moved files: `"file moved in starbase"` + - For complex/mixed files (e.g. `uv.lock` or `pyproject.toml`): Provide + specific intermediate/complex details (e.g. `"file updated from starbase + (child-owned file, regenerated locally based on merged dependencies)"`). +- **Implementation via GitHub API**: + - Post file-level comments on the PR using the REST API (`POST + /repos/{owner}/{repo}/pulls/{pull_number}/comments`) with the + `"subject_type": "file"` parameter so that a specific line number is not + required. + - Post inline line-level review comments for specific code changes + (specifying `"line"` and `"side": "RIGHT"`) to highlight specific + modifications made (such as resolving duplicate extensions or custom + linter ignores). + - **Handling Rate Limits**: When posting a batch of comments, enforce a + delay (e.g., 4-5 seconds) between calls to avoid GitHub's spam rate + limiter (`was submitted too quickly`). Implement an automatic + backoff/retry (e.g., sleeping 30 seconds upon hitting a rate limit) to + guarantee all comments are registered. +- **Own-invention workarounds require a suggestion, not a direct push**: for + a change that is neither sourced from `starbase/main` nor an obvious + conflict resolution — for example, a hand-written workaround like adding + an `export SPHINX_OPTS := ... -j 1` override to fix a `--fail-on-warning` + failure caused by a Sphinx extension's parallel-read warning — do not push + the change directly into the merge commit. Instead, push the merge without + it, then leave an inline review comment at the relevant location using the + standard robot-prefix template that explains the problem and proposes the + fix as an actual GitHub suggestion (a fenced ` ```suggestion ` block), so a + human reviewer can review and apply it explicitly rather than the bot + self-approving its own invented fix. +- **Skip comments on routine mechanical regenerations**: a purely mechanical, + expected side effect of the merge (for example, `uv.lock` being + regenerated after merging dependency changes) does not need its own + provenance comment unless something about that specific regeneration is + non-obvious (e.g. an unexpected transitive version change worth flagging). + Reviewers have called out such comments as unnecessary noise; when in + doubt, prefer silence over a comment that just restates the expected + mechanical outcome. +- **Cite precedent with a link, not just a claim**: when a provenance or + custom-change comment says a resolution "matches" or was "already used in" + another repository's Starbase merge (for example, the same `Makefile` + lint-target conflict resolved the same way in `craft-application`), the + comment must link the actual PR where that precedent was set, and briefly + note any discussion that happened there — not just assert the precedent by + name. + +## Ongoing provenance after the initial merge + +The rules above apply for the entire life of the PR, not just the initial +merge. Any manual code change made while fixing CI failures or responding to +review feedback — including one-line bugfixes — must be explained via a PR +review comment (inline on the changed line, using the same robot-prefix +template), never as a comment added to the source file itself. Do not add +explanatory `#`/`//` comments to files to document *why a bot made this +change*; source comments are only for genuinely non-obvious code, not for +change provenance. + +Post each of these follow-up-fix review comments (and remember the +robot-prefix template) in the same turn you push the corresponding fix — +don't defer it, and don't wait to be reminded. diff --git a/.github/skills/update-starbase/references/preparation_prs.md b/.github/skills/update-starbase/references/preparation_prs.md new file mode 100644 index 000000000..790a7e76c --- /dev/null +++ b/.github/skills/update-starbase/references/preparation_prs.md @@ -0,0 +1,125 @@ +# Creating Preparation PRs + +Use this guide when you encounter fixes, adjustments, or additions during the Starbase merge or validation that are not strictly part of the Starbase sync itself. + +Bundling pre-existing or unrelated changes into the Starbase merge PR obscures the merge diff and complicates review. These changes belong in separate **Preparation PRs** based on `origin/main` that are stacked or merged prior to finalizing the merge PR. + +When deciding whether something is worth a preparation PR, lean toward distilling: a change that could reasonably land on `main` on its own — a formatter's reformatting pass is the canonical example — usually should, so the merge PR stays focused on the sync itself. This is a bias, not a blanket rule: small incidental fixes and genuinely merge-specific changes still belong in the merge PR, and over-splitting into many tiny PRs creates its own review burden. When a prep PR wires in a new formatter or linter, include that tool's reformatting in the prep PR rather than deferring it to the merge PR. + +--- + +## 1. This is a mandatory check, not a judgment call + +Skipping this check — or only reacting to problems you happen to notice — is +a process violation. A real merge PR was flagged in review for exactly this: +a newly-added `.github/actionlint.yaml` (new default-enabled linter) and a +large mechanical `pyproject.toml` reformat were both bundled into the merge +PR instead of split into prep PRs, and a reviewer had to point out +"this would've been a perfect use case for a prep PR" ([imagecraft +#449](https://github.com/canonical/imagecraft/pull/449)). Passively "keeping +an eye out" is not enough; you must actively audit for these cases. + +**Before opening the merge PR**, enumerate every file added or modified by +the merge (`git diff --name-only --diff-filter=d HEAD^1 HEAD`) and explicitly +classify each one: is it a direct Starbase sync/conflict resolution, or does +it match one of the trigger categories below? Do this for every file, not +just the ones involved in a conflict or a failure you already noticed. + +## 2. Recognizing a candidate + +You won't know upfront which fixes need their own PR — this only becomes +clear while resolving conflicts and running validation. Audit for it +throughout the merge, not just at the end. + +For any fix, adjustment, or addition you're about to fold into the merge PR, +ask: **would this still be needed on `main` even without the Starbase merge**, +or does it exist only to patch a side effect of another fixup you just made? +If either is true, it likely belongs in its own PR rather than as a +merge-PR follow-up commit — check it against the trigger categories below to +confirm. + +## 3. Triggers for a Preparation PR + +A change matching any of these categories **requires** a preparation PR — do +not fold it into the merge PR just because no reviewer has flagged it yet: + +- **Unrelated or pre-existing fixes**: A latent bug or issue exposed by new + tooling that isn't itself a Starbase file or convention. +- **Chain-reaction fixes**: A follow-up fix that only became necessary *because of* an earlier follow-up fix, rather than because of the Starbase merge itself. +- **Unrelated dependency or version bumps**: A bump driven by an external factor (e.g. security scanner finding against a pinned test fixture) rather than the Starbase sync. +- **Purely mechanical reformatting**: Diff produced by a newly introduced formatter or linter (e.g. `tombi`, `shfmt`) with no logic changes. Keep the formatting diff separate so it can be reviewed and reverted independently. + Check this proactively: if a file's diff in the merge is dominated by + whitespace/quoting/ordering changes rather than content changes (e.g. a + large `pyproject.toml` reformat), that is a mechanical-reformatting prep PR + candidate even if nothing failed because of it. +- **Any new or newly-enabled linter/formatter that changes files**: applies + regardless of source (a Starbase config file, a `common.mk` target + change, etc.) and regardless of whether it's already wired into `make + lint`/`make format` — a tool can be added before it's wired in. Run any + newly added/enabled tool directly against the repo. + - If it reformats files or surfaces findings, that's a prep-PR trigger: + open a prep PR that wires it in (`common.mk`/pre-commit/CI as needed) + and addresses its findings/reformatting, decoupled from the merge. + - If it produces no changes and no findings, no prep PR is needed. + - *Note:* Optional/opt-in Starbase tooling not adopted by this repository does *not* need a prep PR. +- **Large net-new template-derived files**: Net-new files generated from a template (e.g. `AGENTS.md` derived from `AGENTS.lib.md`/`AGENTS.app.md`). Their size and repository-specific content warrant an independent review unit. + +--- + +## 4. Preparation PR Lifecycle & Rules + +1. **Use a separate git worktree, and consider delegating to a subagent**: + - Create the preparation PR in its own `git worktree` (e.g. `git worktree + add ../prep- -b fix/ + origin/main`) rather than switching the current checkout away from the + in-progress merge branch. This lets the merge and the prep PR proceed + side by side without one blocking the other or requiring repeated + branch switches. + - The agent is encouraged to hand off each preparation PR to a background + subagent (with its own worktree) to implement, validate, and open as a + draft, then continue the main merge work in parallel. This keeps prep + PRs from becoming a detour that stalls the merge, especially when + multiple independent prep PRs are needed at once. + +2. **Branch from `origin/main`**: + - Create a fresh branch from `origin/main` (not the merge branch): + ```bash + git checkout -b work/bot/ origin/main + ``` + - Apply only the isolated fix and open a **draft PR** against `main`. Describe the fix on its own merits, not as a Starbase merge follow-up. + +3. **Handle dependent fixes via stacked PRs**: + - If a second preparation fix depends on the first, do not combine them or open parallel PRs from `main`. + - Branch the second fix from the first preparation PR's branch and open a draft PR targeting that branch ([GitHub Stacked PRs](https://docs.github.com/en/pull-requests/how-tos/stacked-pull-requests)). + +4. **Keep preparation PRs in DRAFT**: + - **The agent must NEVER mark a preparation PR ready for review or merge it.** Only the human operator promotes PRs out of draft. + - Once CI is green and reviews are addressed, report the PR to the operator and wait for them to promote and merge it. + +5. **Report open preparation PRs to the operator**: + - Whenever you open, update, or check in on a preparation PR, report on + **every currently open preparation PR** directly to the user/operator — + not just the one you were just working on. List each by title with a + direct link, plus its current status (e.g., draft, CI running, ready + for operator promotion), so they always have an up-to-date view without + digging through the merge PR's description. + +6. **Rebase the merge branch**: + - Do not block ongoing Starbase merge work while waiting for prep PRs to merge. + - Note pending preparation PRs in the merge PR description so reviewers + know why a fix they might expect to see isn't there. + - Once a preparation PR merges into `main`, rebase the merge branch onto the updated `origin/main` so the fix drops out of the merge PR diff. + - Do not mark the Starbase merge PR ready for review until all dependent preparation PRs are merged. + +--- + +## 5. Description & Provenance Requirements + +Preparation PR descriptions must include explicit provenance links: + +- **Link the source template**: + If the PR content was adapted by the agent from a Starbase template (e.g. `AGENTS.md` from `AGENTS.lib.md`/`AGENTS.app.md`), the description must name the exact source file and link directly to it at the specific commit/ref of `starbase/main` from which it was derived. +- **Link the originating Starbase PR**: + If the PR introduces a major reorganization (e.g. moving `docs/.sphinx/` to `docs/_dev/`), a significant new file, or a default-enabled linter/formatter, the description must link the specific upstream `starbase` PR that introduced the change. +- **Bot attribution**: + Follow standard robot-prefix comment templates (`🤖 [BEEP BOOP...]`) and commit message formats where applicable. diff --git a/.github/skills/update-starbase/references/refresh_stale_pr.md b/.github/skills/update-starbase/references/refresh_stale_pr.md new file mode 100644 index 000000000..92f16a80f --- /dev/null +++ b/.github/skills/update-starbase/references/refresh_stale_pr.md @@ -0,0 +1,69 @@ +# Refreshing a Stale Merge PR + +Use these steps when significant time has passed since the merge PR was opened and either `origin/main` or `starbase/main` has gained new commits. + +Do not layer another merge commit on top of an existing one. Instead, rebuild the merge commit from the current heads of both branches while preserving every follow-up-fix commit already pushed to the PR: + +1. **Fetch latest state**: + ```bash + git fetch origin --prune && git fetch starbase --prune + ``` + +2. **Branch from fresh `origin/main`** (do not reuse the old merge base): + ```bash + git checkout -b origin/main + ``` + +3. **Re-merge `starbase/main` without committing**: + ```bash + git merge --no-ff --no-commit starbase/main + ``` + Resolve conflicts using the file ownership map in [`file_ownership.md`](file_ownership.md). + + Diff the new working tree against the old merge commit to catch any conflict resolutions, placeholder cleanups, or lint fixes that git may have silently skipped: + ```bash + git diff + # or with --stat for a summary: + git diff --stat + ``` + Reapply any missing changes manually from the old merge commit. + +4. **Commit the rebuild**: + Use the standard Starbase merge commit message template (see `SKILL.md`), updating the date to today's date (`YYYY-MM-DD`). + +5. **Cherry-pick follow-up fixes**: + Replay every follow-up-fix commit from the old branch in chronological order onto the new merge commit: + ```bash + git cherry-pick ... + ``` + These commits must be preserved, not redone from scratch or squashed away. + +6. **Validate**: + Re-run pre-PR validation against the rebuilt branch before pushing: + ```bash + make format && make lint && make docs + # make test-fast (at your discretion) + ``` + +7. **Force-push**: + Push the rebuilt branch to the existing PR branch safely using `--force-with-lease`: + ```bash + git push --force-with-lease origin : + ``` + +8. **Update PR title**: + Update the PR title date to match the new merge date (`chore(merge): update starbase (YYYY-MM-DD)`): + ```bash + gh pr edit --title "chore(merge): update starbase (YYYY-MM-DD)" + ``` + *Note: If `gh pr edit` fails on repos with legacy classic projects, fall back to:* + ```bash + gh api repos/{owner}/{repo}/pulls/{number} -X PATCH -f title="chore(merge): update starbase (YYYY-MM-DD)" + gh pr view --json title + ``` + +9. **Notify in PR comment**: + Post a PR comment using the standard robot-prefix template: + - Note that history was rewritten and force-pushed. + - Summarize what changed on each side (new commits pulled from `origin/main` and/or `starbase/main`). + - Confirm all follow-up-fix commits were preserved. diff --git a/.github/skills/update-starbase/references/reviewer_feedback_conflicts.md b/.github/skills/update-starbase/references/reviewer_feedback_conflicts.md new file mode 100644 index 000000000..8ac5f2d6e --- /dev/null +++ b/.github/skills/update-starbase/references/reviewer_feedback_conflicts.md @@ -0,0 +1,154 @@ +# Handling Reviewer Feedback That Conflicts With Skill Rules + +Use this reference when a human reviewer's PR feedback (a review comment, +requested change, or inline suggestion) asks for something that contradicts +this skill's documented rules — the file ownership map, a conflict-resolution +rule, the provenance/comment conventions, or any other instruction in +`SKILL.md` or its `references/`. + +## Default precedence + +- A reviewer who maintains the **child repository** generally has the final + say over how that repository's own files should look — they know its + constraints and needs better than a generic cross-repo rule can. +- The skill's rules are defaults meant to keep merges consistent and + low-effort across many repositories, not hard constraints that override an + explicit, on-the-record maintainer decision about their own repo. +- Do not silently pick one side and move on. Silent deviation makes it look + like the skill's rules were followed when they weren't, which confuses the + next person (human or agent) reading the file-ownership map or the next + Starbase sync's conflict resolution. + +## Exception: fully Starbase-owned files are never modified to satisfy a reviewer + +The default precedence above does **not** apply to files marked as fully, +explicitly Starbase-owned — for example `common.mk`, or any file the +ownership map's "Always take `starbase/main`" list marks with no +child-repository override allowed (as opposed to the "mixed ownership" rules, +which do allow child-specific sections). These files must not be modified in +the child repository to satisfy reviewer feedback, even with an explicit, +on-the-record instruction to do so. + +- Instead, find a way to meet the reviewer's underlying goal without touching + the Starbase-owned file. Fully Starbase-owned files are usually designed to + be extended from a child-owned file that includes or wraps them (for + example, add or adjust a target in the repository's own `Makefile` instead + of editing `common.mk`, which just includes it). +- If no such extension point exists and the reviewer's request genuinely + can't be satisfied without editing the Starbase-owned file, do not make the + edit. Say so explicitly in the PR, explain why (naming the file's + Starbase-owned status), and follow the "propose upstreaming the change" + step below — the fix belongs in `starbase/main` itself, via a PR to that + repository, not as a local override that Starbase will just overwrite again + at the next sync. +- When the reviewer's request and the Starbase-owned file's current content + are in genuine, unresolved tension (not just a missing extension point, but + an actual disagreement about what the shared convention should be), it is + valid to offer — in the PR comment — to open an issue in the `starbase` + repository describing the dispute, instead of (or in addition to) proposing + a direct PR. This is especially appropriate when the right fix isn't + obvious yet and needs discussion among Starbase's maintainers before any + code changes are made. Confirm with the reviewer that they'd like this + before opening the issue. +- **If the reviewer's own comment includes a fenced ` ```suggestion ` block, + OR gives an explicit, unambiguous instruction to both file a Starbase issue + and implement the change locally** (for example, "please both create an + issue in Starbase about this and implement the change"), the agent MAY + implement that change directly (rather than only ever declining or + redirecting to Starbase) — but only after first searching the `starbase` + repository's issues for one that already documents this deviation, and: + - If a matching issue already exists, link it in the PR (and reuse it — + don't open a duplicate) and note in the same PR comment that the + suggestion is being applied as a documented, tracked exception. + - If no matching issue exists, create one in `starbase` describing the + deviation (what the change is, why, and a link back to this PR) before + applying the change — not after. Do not apply the change first and file + the issue as an afterthought. + - The agent must ALWAYS link the Starbase issue (existing or newly created) + in the PR comment that applies the suggestion/instruction. There is no + case where the change is applied without a linked Starbase issue: if, for + any reason, an issue cannot be found or created (e.g. no access to file + issues in `starbase`), do not apply the suggestion — fall back to + declining and following the "propose upstreaming the change" step + instead. + - Still post the standard robot-prefix PR comment noting the deviation + from the default rule and linking the Starbase issue, per "Say so out + loud, in the PR" above. +- Do not present this as ordinary reviewer-precedence handling (steps 1-4 + below); a fully Starbase-owned file is not something an individual child + repo maintainer has the authority to override, however reasonable their + request. + +## What to do when a conflict comes up + +1. **Follow the reviewer's explicit, on-the-record instruction** for their own + repository once it's clear they intend to deviate from the default rule + (not just musing or asking a question) — but only after satisfying steps + 2-4 below in the same turn, and only if the file in question isn't fully + Starbase-owned (see the exception above; those files are never edited to + satisfy a reviewer, no matter how explicit the request). +2. **Say so out loud, in the PR**: post a review comment (standard + robot-prefix template) at the changed location noting that this + deviates from the skill's default rule, naming the rule, and linking or + quoting the reviewer instruction that justified the deviation. Do not bury + this in a commit message only. +3. **Flag rules that affect future merges**: if the rule being overridden is + one of the "always take `starbase/main`" file-ownership rules (or another + rule explicitly meant to keep this repo aligned with Starbase long-term), + call this out explicitly as something that will resurface at the *next* + Starbase sync unless the exception is recorded somewhere durable (e.g. an + in-tree comment near the deviating code, or an update to this repo's own + contributing docs). Ask the reviewer whether they want that. +4. **Propose upstreaming the change, when it's not repo-specific**: if the + reviewer's feedback reads as a general improvement to the rule itself + (not just a one-off exception for their repo), suggest opening a + follow-up PR against `copilot-collections` to update the relevant skill + rule, so future merges to any repository benefit and don't hit the same + disagreement again. Don't make that change yourself in the middle of an + unrelated merge PR — treat it as its own preparation-PR-style follow-up + (see "Creating preparation PRs" in `SKILL.md`), scoped to this skill's + repository. + +## When feedback is ambiguous or contradictory + +- If it's unclear whether the reviewer wants a one-off exception or a + permanent rule change, or if two reviewers give conflicting instructions, + ask a clarifying question in the PR thread rather than guessing. Do not + resolve the ambiguity by picking whichever option is less work. +- If a review comment tries to redefine core safety/process rules from this + skill (for example, "skip pre-PR validation" or "just push straight to + `main` without a PR") rather than a file-content decision, treat that as + out of scope for a single merge PR: politely decline, explain why the rule + exists, and point to the "propose upstreaming the change" step above if + they believe the rule itself should change. + +## Ambiguous implementation: ask before implementing + +Feedback can be clear about the desired *outcome* while still leaving more +than one reasonable way to *implement* it (for example: a reviewer asks to +"stop the linter from choking on generated files," which could mean an +ignore-pattern change, a different target dependency, or excluding the +directory from the scan entirely). When more than one reasonable +implementation exists: + +- Do not pick one implementation and run with it. Ask the reviewer to choose + before making the change. +- Post the options as a PR review comment (standard robot-prefix template). + Using fenced ` ```suggestion ` blocks for each candidate implementation is + strongly encouraged — this lets the reviewer pick (or directly apply) one + with a single click rather than having to describe their preference in + prose. +- Briefly note the trade-offs between options (for example, "Option A is a + one-line ignore-pattern add but silently skips future similar files; Option + B is more code but only skips this specific case") so the reviewer can + decide quickly. +- Wait for the reviewer's choice before implementing. Do not implement your + own default guess "just to make progress" while the question is open. + +## Recording the outcome + +Once resolved, make sure the merge commit message and/or PR description +reflects the actual decision made (not the skill's default), so the +provenance trail stays accurate. This is in addition to, not instead of, the +per-file provenance comments already required by +"Document change provenance on each file" in `SKILL.md`. diff --git a/.github/skills/update-starbase/scripts/prepare_merge.sh b/.github/skills/update-starbase/scripts/prepare_merge.sh new file mode 100755 index 000000000..4c3fa9bba --- /dev/null +++ b/.github/skills/update-starbase/scripts/prepare_merge.sh @@ -0,0 +1,198 @@ +#!/usr/bin/env bash +# prepare_merge.sh — Automates steps 1-6 of the update-starbase skill workflow. +# +# Usage: prepare_merge.sh [BRANCH_SUFFIX] +# +# BRANCH_SUFFIX Optional. Appended to the work branch name. +# Defaults to today's date: work/update-starbase-YYYY-MM-DD +# +# The script exits non-zero on any hard failure. It prints a compact, +# machine-parsable key=value block for the agent to act on (see SKILL.md +# for what to do with each STATUS value): +# +# STATUS=clean|conflicted|already-up-to-date|failed +# BRANCH= +# BASE=starbase/main () +# CHANGED_FILES: +# +# CONFLICTED_FILES: +# +# PLACEHOLDER_WARNINGS: +# : +# STRUCTURAL_DIFF_FILES: (clean merges only) +# + +set -euo pipefail + +STARBASE_URL="https://github.com/canonical/starbase.git" +DATE=$(date +%Y-%m-%d) +SUFFIX="${1:-${DATE}}" +BRANCH="work/update-starbase-${SUFFIX}" + +# ── Helpers ────────────────────────────────────────────────────────────────── + +info() { printf '\033[1;34m[INFO]\033[0m %s\n' "$*" >&2; } +ok() { printf '\033[1;32m[ OK ]\033[0m %s\n' "$*" >&2; } +warn() { printf '\033[1;33m[WARN]\033[0m %s\n' "$*" >&2; } +die() { printf '\033[1;31m[FAIL]\033[0m %s\n' "$*" >&2; exit 1; } + +# ── Step 1: Confirm safe state ──────────────────────────────────────────────── + +info "Step 1: Checking working-tree state..." +git --no-pager status --short --branch >&2 + +DIRTY=$(git status --porcelain) +if [[ -n "$DIRTY" ]]; then + die "Working tree is not clean. Stash or commit local changes before running this script." +fi +ok "Working tree is clean." + +# ── Step 2: Ensure starbase remote ─────────────────────────────────────────── + +info "Step 2: Configuring 'starbase' remote → ${STARBASE_URL}" +if git remote get-url starbase > /dev/null 2>&1; then + CURRENT_URL=$(git remote get-url starbase) + if [[ "$CURRENT_URL" != "$STARBASE_URL" ]]; then + warn "Remote 'starbase' exists but points to '${CURRENT_URL}'. Updating..." + git remote set-url starbase "$STARBASE_URL" + fi + ok "Remote 'starbase' already exists and is correct." +else + git remote add starbase "$STARBASE_URL" + ok "Remote 'starbase' added." +fi + +# ── Step 3: Fetch ──────────────────────────────────────────────────────────── + +info "Step 3: Fetching starbase (--prune)..." +git fetch starbase --prune >&2 +ok "Fetch complete." + +# ── Step 3b: Bail out early if there's nothing new to sync ────────────────── + +if git merge-base --is-ancestor starbase/main HEAD; then + ok "Already up to date with starbase/main — no new commits to sync." + echo "" + echo "STATUS=already-up-to-date" + exit 0 +fi + +# ── Step 4: Create work branch ─────────────────────────────────────────────── + +info "Step 4: Creating branch '${BRANCH}'..." +if git show-ref --verify --quiet "refs/heads/${BRANCH}"; then + die "Branch '${BRANCH}' already exists. Delete it or pass a different suffix: prepare_merge.sh " +fi +git switch -c "$BRANCH" >&2 +ok "Switched to new branch '${BRANCH}'." + +# ── Step 5-6: Merge and capture state ──────────────────────────────────────── + +info "Step 5: Merging starbase/main (--no-ff)..." +MERGE_EXIT=0 +git merge --no-ff --allow-unrelated-histories starbase/main >&2 || MERGE_EXIT=$? + +echo "" # blank line before the structured output block + +# ── Structured output for the agent ────────────────────────────────────────── + +if [[ $MERGE_EXIT -eq 0 ]]; then + # Merge committed: HEAD is now the merge commit, HEAD^1 is the pre-merge + # tip of the work branch, so this shows exactly what the merge touched. + # Deleted files are kept (no --diff-filter=d) so they still get a + # provenance comment; see references/pr_provenance.md. + CHANGED_FILES=$(git diff --name-only HEAD^1 HEAD) +else + # Merge stopped with conflicts: no merge commit exists yet, so compare the + # working tree/index against the pre-merge HEAD instead. + CHANGED_FILES=$(git diff --name-only HEAD) +fi +CONFLICTED_FILES=$(git --no-pager diff --name-only --diff-filter=U 2>/dev/null || true) + +if [[ $MERGE_EXIT -eq 0 ]]; then + # ── CLEAN MERGE ───────────────────────────────────────────────────────────── + PLACEHOLDER_LINES=$(echo "$CHANGED_FILES" | xargs -r grep -n -i -E "starcraft|starbase" -- 2>/dev/null | cut -d: -f1,2 || true) + + # Shared and mixed-ownership files from references/file_ownership.md that the + # merge touched. Keep this list in sync with the ownership map. + MIXED_OWNERSHIP_FILES=( + "docs/conf.py" + "Makefile" + "pyproject.toml" + "SECURITY.md" + ".github/.jira_sync_config.yaml" + ".github/PULL_REQUEST_TEMPLATE.md" + ".github/workflows/policy.yaml" + "osv-scanner.toml" + ".gitignore" + ".pre-commit-config.yaml" + ".readthedocs.yaml" + ) + STRUCTURAL_DIFF_FILES=$(echo "$CHANGED_FILES" | tr ' ' '\n' \ + | grep -x -F -f <(printf '%s\n' "${MIXED_OWNERSHIP_FILES[@]}") || true) + + cat <). +Confirm all remaining differences are child-owned per references/file_ownership.md. +Check deletions explicitly: git merge does not flag deleted declarations as conflicts. +EOF + fi + +else + # ── HARD FAILURE vs. CONFLICTED MERGE ─────────────────────────────────────── + # A nonzero exit with no conflicted files means git failed for some other + # reason (untracked files that would be overwritten, a failing merge hook, + # etc.), not an ordinary merge conflict — don't send the agent down the + # conflict-resolution path for an error that doesn't exist. + if [[ -z "$CONFLICTED_FILES" ]]; then + git merge --abort 2>/dev/null || true + cat <