--- quick_id: 260613-fp9 title: ".gitea and .planning pushes should not trigger a docker image publish" status: complete date: 2026-06-13 --- # Quick Task 260613-fp9 — Summary ## What changed Added a `paths-ignore` filter to the `push` trigger in `.gitea/workflows/publish.yml`: ```yaml on: push: branches: [main] paths-ignore: - '.gitea/**' - '.planning/**' ``` Updated the file's header comment to document the new skip behavior. ## Why Every push to `main` previously ran a full Docker build + push. Pushes confined to `.planning/**` (planning docs push straight to main under the unprotected `.planning/*` branch-protection pattern) or `.gitea/**` (CI/workflow edits) never change the shipped image — `.dockerignore` already excludes `.planning`. The rebuild and `:latest` re-push were wasted runner time. ## Behavior - Push touching only `.gitea/**` and/or `.planning/**` → `publish` job skipped. - Push touching code / Dockerfile / manifests (alone or mixed with docs) → `publish` runs as before. `paths-ignore` skips only when **every** changed file matches a glob. ## Verification - `python3 yaml.safe_load` parses the file; `on.push` carries both `branches: [main]` and `paths-ignore: ['.gitea/**', '.planning/**']`; the `publish` job is intact. - The in-workflow `grep "--target production"` self-assertion still matches (D-10 hygiene check unaffected). ## Isolation note Executed in a dedicated worktree (`familysync-wt-fp9`, branch `quick/260613-fp9-publish-paths-ignore` off `origin/main`) because a concurrent phase-10 agent has the main working tree checked out on `gsd/phase-10-admin-role-settings`. No subagents spawned — trivial single-file config edit done inline. ## Follow-up fix — ci.yml heavy-job paths-filter (added after PR opened) Opening PR #16 surfaced a second, related bug: the `api` + `harness` jobs ran on this `.gitea`/`.planning`-only PR. Root cause in `.gitea/workflows/ci.yml`'s `changes` job: - dorny/paths-filter combines a filter's patterns with `Array.some`, and picomatch compiles `!.gitea/**` as "matches any path **not** under `.gitea`". - So the `- '!.gitea/**'` line (added by quick task 260613-dmw) matched every non-`.gitea` file — including `.planning/**` and `*.md` — making `code=true` for doc-only PRs and silently running the heavy jobs. It also never excluded `.gitea` (the `**/*.yml` glob already matched workflow files), so dmw's stated goal was never achieved and it regressed the Phase 15 doc-only skip. Fix: switched the filter to `predicate-quantifier: 'every'` with negation-only globs so a file counts as `code` ONLY if it is outside `.gitea/`, outside `.planning/`, and not Markdown: ```yaml predicate-quantifier: 'every' filters: | code: - '!.gitea/**' - '!.planning/**' - '!**/*.md' ``` Verified locally with picomatch against representative file sets: `.gitea`-only, `.planning`-only, `*.md`-only, and this PR's mix all → `code=false` (heavy jobs skip); real code (`.ts`), `pnpm-lock.yaml`, `package.json`, and code+docs mixes all → `code=true` (heavy jobs run). ## Follow-up `publish.yml` and `ci.yml` both live under `.gitea/**`, so merging this PR is a `.gitea`-only push and will (correctly) not publish. PR #16 opened against `main` (protected; code changes require PR).