diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 6e50bbc6..b82544ca 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -39,7 +39,7 @@ { "name": "engineering-practices", "description": "Git commits, conventional releases, refactoring, test-driven development, and engineering ways of working skills", - "version": "1.1.2", + "version": "1.1.3", "source": "./plugins/engineering-practices" }, { diff --git a/.github/plugin/marketplace.json b/.github/plugin/marketplace.json index 6e50bbc6..b82544ca 100644 --- a/.github/plugin/marketplace.json +++ b/.github/plugin/marketplace.json @@ -39,7 +39,7 @@ { "name": "engineering-practices", "description": "Git commits, conventional releases, refactoring, test-driven development, and engineering ways of working skills", - "version": "1.1.2", + "version": "1.1.3", "source": "./plugins/engineering-practices" }, { diff --git a/.github/workflows/update-agent-skills.yaml b/.github/workflows/update-agent-skills.yaml index c228e552..feb1393a 100644 --- a/.github/workflows/update-agent-skills.yaml +++ b/.github/workflows/update-agent-skills.yaml @@ -10,12 +10,18 @@ on: options: - all - agentic-engineering + - engineering-practices schedule: - cron: "0 6 * * *" -permissions: - contents: write - pull-requests: write +permissions: {} + +# All scopes can update the same per-skill branches. Keep their digest/version +# follow-ups inside the same non-cancelling queue as the updater itself. +concurrency: + group: agent-plugins-skill-sync-${{ github.repository }} + cancel-in-progress: false + queue: max jobs: scope: @@ -40,6 +46,9 @@ jobs: update: needs: scope + permissions: + contents: write # The shared updater writes the signed skill-sync commits. + pull-requests: write # The shared updater opens per-skill update proposals. uses: devantler-tech/.github/.github/workflows/update-agent-skills.yaml@e271b7ff25d4ce97b067612e7ba0c5d657b6a17a # v6.1.0 with: dir: ${{ needs.scope.outputs.dir }} diff --git a/AGENTS.md b/AGENTS.md index 7194e096..b5a4290b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -128,8 +128,8 @@ which records the true upstream in the skill's `metadata.github-*` frontmatter ( `anthropics/skills`, our own sibling [`devantler-tech/agent-skills`](https://github.com/devantler-tech/agent-skills), …), each tracked independently. The daily [`update-agent-skills.yaml`](.github/workflows/update-agent-skills.yaml) workflow runs -[`gh skill update --all`](https://github.com/devantler-tech/actions/tree/main/update-agent-skills) via -the [`update-agent-skills`](https://github.com/devantler-tech/actions/blob/main/.github/workflows/update-agent-skills.yaml) +[`gh skill update --all`](https://github.com/devantler-tech/.github/tree/main/actions/update-agent-skills) via +the [`update-agent-skills`](https://github.com/devantler-tech/.github/blob/main/.github/workflows/update-agent-skills.yaml) reusable workflow and opens one PR per drifted skill, from `deps/agent-skills-update-`, so a blocked update holds back only its own skill — **no lockfile, no sync bot, no custom metadata.** Never hand-edit anything inside a bundled skill — not the `SKILL.md`, and not the `references/`, `scripts/` and `assets/` files beside it, which are equally the upstream's and equally @@ -143,9 +143,13 @@ distributed notice. A changed notice or incomplete read refuses validation; refr a reviewed curation change rather than replacing the notice silently. Run `bash scripts/check-matt-skill-license.test.sh` for its offline source and notice regressions. For a portfolio-owned repair, dispatch `update-agent-skills.yaml` with -`scope=agentic-engineering` to update only that plugin's skills through the same programmed -PRs. The default `all` scope and scheduled updates cover the full catalogue. Unsupported +`scope=agentic-engineering` or `scope=engineering-practices` to update only the selected plugin's +skills through the same programmed, per-skill PRs. Each scope retains the canonical branch +identity and existing one-skill edit boundary. The default `all` scope and scheduled updates +cover the full catalogue. Unsupported scope observations refuse before the updater starts. +All scopes share one non-cancelling workflow queue, including their digest, version and release-note +follow-ups, so overlapping selections cannot rewrite the same update branch concurrently. `guard-bundled-skill-edits.sh` covers the rest of the tree: a PR that changes any file inside a synced skill fails and names the upstream to fix it in, so the edit is refused at review instead of being silently reverted by the next sync. The programmed sync PR is exempt for its own skill, a wholly new skill diff --git a/docs/scripts.md b/docs/scripts.md index 3cbcf0cd..6a81c132 100644 --- a/docs/scripts.md +++ b/docs/scripts.md @@ -8,6 +8,7 @@ complete inventory; read a script's header for its arguments and safeguards. | Check a change | Follow the [validation steps](validation.md), including dependency installation | | Update versions and release notes | Use [bump-plugin-version.sh](../scripts/bump-plugin-version.sh) and add a matching changelog entry; skill syncs use [plugin-changelog.sh write](../scripts/plugin-changelog.sh) | | Refresh generated resource digests | [refresh-desired-state-digests.sh](../scripts/refresh-desired-state-digests.sh) | +| Synchronize one curated plugin | Dispatch [Update Agent Skills](../.github/workflows/update-agent-skills.yaml) with `agentic-engineering` or `engineering-practices`; [skill-update-scope.sh](../scripts/skill-update-scope.sh) resolves only declared selections before the normal per-skill updater runs. Default and scheduled `all` updates still cover the full catalogue. | | Prepare or inspect a marketplace release | [Release guide](marketplace-releases.md) and [prepare-marketplace-release.sh](../scripts/prepare-marketplace-release.sh) | | Propose or publish a marketplace release | [Release guide](marketplace-releases.md), which explains the separate opt-in steps and recovery | | Recheck open pull requests after changing CI | [recheck-open-prs.sh](../scripts/recheck-open-prs.sh) | diff --git a/plugins/engineering-practices/.claude-plugin/plugin.json b/plugins/engineering-practices/.claude-plugin/plugin.json index fe28c8ae..08dd0cdb 100644 --- a/plugins/engineering-practices/.claude-plugin/plugin.json +++ b/plugins/engineering-practices/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "engineering-practices", "description": "Git commits, conventional releases, refactoring, test-driven development, and engineering ways of working skills", - "version": "1.1.2", + "version": "1.1.3", "author": { "name": "devantler-tech", "url": "https://github.com/devantler-tech" diff --git a/plugins/engineering-practices/CHANGELOG.md b/plugins/engineering-practices/CHANGELOG.md index 0ef4263d..44fd5216 100644 --- a/plugins/engineering-practices/CHANGELOG.md +++ b/plugins/engineering-practices/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog — `engineering-practices` +## 1.1.3 — 2026-10-23 + +**Changed** — sync `ways-of-working` from `https://github.com/devantler-tech/agent-skills` at `refs/tags/v1.27.1`. + ## 1.1.2 — 2026-10-15 **Changed** — sync `write-readme` from `https://github.com/devantler-tech/agent-skills` at `refs/tags/v1.24.1`. diff --git a/plugins/engineering-practices/plugin.json b/plugins/engineering-practices/plugin.json index fe28c8ae..08dd0cdb 100644 --- a/plugins/engineering-practices/plugin.json +++ b/plugins/engineering-practices/plugin.json @@ -1,7 +1,7 @@ { "name": "engineering-practices", "description": "Git commits, conventional releases, refactoring, test-driven development, and engineering ways of working skills", - "version": "1.1.2", + "version": "1.1.3", "author": { "name": "devantler-tech", "url": "https://github.com/devantler-tech" diff --git a/plugins/engineering-practices/skills/ways-of-working/SKILL.md b/plugins/engineering-practices/skills/ways-of-working/SKILL.md index 8a3322c9..8c90c839 100644 --- a/plugins/engineering-practices/skills/ways-of-working/SKILL.md +++ b/plugins/engineering-practices/skills/ways-of-working/SKILL.md @@ -3,9 +3,9 @@ description: 'Codifies devantler-tech engineering practices: agent-first develop license: Apache-2.0 metadata: github-path: ways-of-working - github-ref: refs/tags/v1.5.0 + github-ref: refs/tags/v1.27.1 github-repo: https://github.com/devantler-tech/agent-skills - github-tree-sha: 219a45e80e25b47cc23dcfed9febd526655e624c + github-tree-sha: d079ead0a238b051a081616f3bcb0c0b557f518d name: ways-of-working --- # Ways of Working @@ -90,7 +90,13 @@ Use **GitHub Flow**: ## CI/CD Pipeline -Each repository has two core workflow files — `ci.yaml` and `cd.yaml` — plus a thin `release.yaml` that calls a reusable workflow (see [Releases](#releases)). Reusable logic is extracted into local GitHub Actions under `.github/actions/`: +Keep ordinary workflow entrypoints in the repository that owns their outcome, normally `ci.yaml`, `cd.yaml` and, where needed, `release.yaml`. Choose ownership by the actual production callers: + +- **Organization-required workflows:** cross-product PR checks such as shared scanning and linting. A guard used by one product belongs in that product, enforced through its repository ruleset and native required checks. +- **Reusable workflows:** job-level logic called by at least two genuine production jobs. Two real entrypoints in one repository can qualify; matrix cells, test fixtures and invented callers do not establish reuse. +- **Ordinary workflows:** callsites for reusable jobs, or implementations used in one place. Keep a single-use implementation in its calling repository rather than exporting it from a shared automation repository. + +Split manageable step-level units into local GitHub Actions under `.github/actions/` when useful: - **Composite actions** — for simple, self-contained steps. - **TypeScript actions** — for anything complex, because they are locally testable and avoid hard-to-read bash embedded in YAML. @@ -114,7 +120,7 @@ Each repository has two core workflow files — `ci.yaml` and `cd.yaml` — plus ### Releases -Releases are automated via the [`devantler-tech/actions/.github/workflows/create-release.yaml`](https://github.com/devantler-tech/actions/blob/main/.github/workflows/create-release.yaml) reusable workflow. It runs [semantic-release](https://semantic-release.gitbook.io/) on `push` to `main`, calculates the next semver from [conventional commit](https://www.conventionalcommits.org/) history, and creates the tag + GitHub release — which in turn triggers the CD pipeline above. +Repositories using the shared release job invoke [`devantler-tech/.github/.github/workflows/create-release.yaml`](https://github.com/devantler-tech/.github/blob/main/.github/workflows/create-release.yaml) from a repository-owned caller that handles `push` to `main`. The shared workflow runs [semantic-release](https://semantic-release.gitbook.io/), calculates the next semver from [conventional commit](https://www.conventionalcommits.org/) history, and creates the tag and GitHub release, which can trigger the repository's CD pipeline. Preserve a repository-owned release implementation when its requirements differ; a release entrypoint does not by itself justify a new reusable workflow. For application publishing, always use upstream releasers — e.g. [GoReleaser](https://goreleaser.com/) for Go or [dotnet-releaser](https://github.com/xoofx/dotnet-releaser) for .NET — rather than hand-rolling publish scripts. diff --git a/scripts/skill-update-scope.sh b/scripts/skill-update-scope.sh index bafc8440..885a47ae 100644 --- a/scripts/skill-update-scope.sh +++ b/scripts/skill-update-scope.sh @@ -6,5 +6,6 @@ refuse() { printf 'skill update scope: unsupported dispatch\n' >&2; exit 2; } case "$1:${2:-all}" in schedule:all|workflow_dispatch:all) printf 'dir=plugins\nbranch=deps/agent-skills-update\n' ;; workflow_dispatch:agentic-engineering) printf 'dir=plugins/agentic-engineering/skills\nbranch=deps/agent-skills-update-agentic-engineering-skills\n' ;; + workflow_dispatch:engineering-practices) printf 'dir=plugins/engineering-practices/skills\nbranch=deps/agent-skills-update-engineering-practices-skills\n' ;; *) refuse ;; esac diff --git a/scripts/skill-update-scope.test.sh b/scripts/skill-update-scope.test.sh index 89803972..fa2d4ee1 100644 --- a/scripts/skill-update-scope.test.sh +++ b/scripts/skill-update-scope.test.sh @@ -12,15 +12,38 @@ bash "$here/skill-update-scope.sh" workflow_dispatch all > "$work/out" [[ $(cat "$work/out") == $'dir=plugins\nbranch=deps/agent-skills-update' ]] bash "$here/skill-update-scope.sh" workflow_dispatch agentic-engineering > "$work/out" [[ $(cat "$work/out") == $'dir=plugins/agentic-engineering/skills\nbranch=deps/agent-skills-update-agentic-engineering-skills' ]] -for scope in unknown ../outside $'agentic-engineering\ndir=plugins'; do +bash "$here/skill-update-scope.sh" workflow_dispatch engineering-practices > "$work/out" +[[ $(cat "$work/out") == $'dir=plugins/engineering-practices/skills\nbranch=deps/agent-skills-update-engineering-practices-skills' ]] +# A scoped producer must still create the canonical per-skill branch accepted +# by the existing actor-and-one-skill guard, not a broader exemption. +branch=$(sed -n 's/^branch=//p' "$work/out") +[[ "$branch-ways-of-working" == deps/agent-skills-update-engineering-practices-skills-ways-of-working ]] +for scope in unknown ../outside $'agentic-engineering\ndir=plugins' $'engineering-practices\nbranch=other'; do rc=0 bash "$here/skill-update-scope.sh" workflow_dispatch "$scope" > "$work/out" 2> "$work/err" || rc=$? [[ $rc == 2 && ! -s $work/out && -s $work/err ]] done rc=0 -bash "$here/skill-update-scope.sh" schedule agentic-engineering > "$work/out" 2> "$work/err" || rc=$? -[[ $rc == 2 && ! -s $work/out ]] +for scope in agentic-engineering engineering-practices; do + rc=0 + bash "$here/skill-update-scope.sh" schedule "$scope" > "$work/out" 2> "$work/err" || rc=$? + [[ $rc == 2 && ! -s $work/out ]] +done rc=0 bash "$here/skill-update-scope.sh" pull_request all > "$work/out" 2> "$work/err" || rc=$? [[ $rc == 2 && ! -s $work/out ]] -printf 'skill update scopes: PASS (9 cases)\n' +options=$(yq -o=json '.on.workflow_dispatch.inputs.scope' "$here/../.github/workflows/update-agent-skills.yaml") +printf '%s\n' "$options" | jq -se 'length == 1 and (.[0] | .type == "choice" and .default == "all" and + (.options | sort) == ["agentic-engineering","all","engineering-practices"])' > /dev/null +workflow=$(yq -o=json '.' "$here/../.github/workflows/update-agent-skills.yaml") +if ! printf '%s\n' "$workflow" | jq -se 'length == 1 and (.[0].concurrency == { + group:"agent-plugins-skill-sync-${{ github.repository }}", + "cancel-in-progress":false, queue:"max"})' > /dev/null; then + printf 'skill update scopes: require one non-cancelling queue across all scopes and follow-up jobs\n' >&2 + exit 1 +fi +printf '%s\n' "$workflow" | jq -se 'length == 1 and (.[0] | + .permissions == {} and .jobs.scope.permissions == {contents:"read"} and + .jobs.update.permissions == {contents:"write","pull-requests":"write"} and + .jobs."bump-versions".permissions == {contents:"read"})' > /dev/null +printf 'skill update scopes: PASS (declared selections, canonical branch and fail-closed dispatch)\n'