From dfd85c829dd3453494a7d000760b7985fbb6160f Mon Sep 17 00:00:00 2001 From: DJ Date: Tue, 7 Apr 2026 20:40:46 -0700 Subject: [PATCH 1/4] chore(workflows): adopt centralized stubs from petry-projects/.github Replace inline copies of standardized workflows with the canonical thin caller stubs from petry-projects/.github/standards/workflows/. Each stub delegates to a versioned reusable workflow at petry-projects/.github/.github/workflows/-reusable.yml@v1, so future updates to the standard propagate automatically and drift is caught by the org-wide compliance audit. See petry-projects/.github#87, #88, #89 for context. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/claude.yml | 24 ++- .github/workflows/dependabot-automerge.yml | 85 +++----- .github/workflows/dependabot-rebase.yml | 137 +++---------- .github/workflows/dependency-audit.yml | 223 ++------------------- .github/workflows/feature-ideation.yml | 72 ++++--- 5 files changed, 130 insertions(+), 411 deletions(-) diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 70bfde0f..3faf303c 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -1,6 +1,24 @@ +# ───────────────────────────────────────────────────────────────────────────── +# SOURCE OF TRUTH: petry-projects/.github/standards/workflows/claude.yml +# Standard: petry-projects/.github/standards/ci-standards.md#4-claude-code-claudeyml +# Reusable: petry-projects/.github/.github/workflows/claude-code-reusable.yml +# +# AGENTS — READ BEFORE EDITING: +# • This file is a THIN CALLER STUB. All Claude Code logic, the prompt, +# allowedTools, and trigger gating live in the reusable workflow above. +# • You MAY change: nothing in this file in normal use. Adopt verbatim. +# • You MUST NOT change: trigger events, job permissions, the `uses:` line, +# or `secrets: inherit`. These are required for the reusable to work. +# • If you need different behaviour, open a PR against the reusable in the +# central repo. The change will propagate everywhere on next run. +# ───────────────────────────────────────────────────────────────────────────── +# # Claude Code — thin caller that delegates to the org-level reusable workflow. -# All logic and prompts are maintained centrally in claude-code-reusable.yml. -# Standard: https://github.com/petry-projects/.github/blob/main/standards/ci-standards.md#4-claude-code-claudeyml +# To adopt: copy this file to .github/workflows/claude.yml in your repo. +# Required org/repo secret: CLAUDE_CODE_OAUTH_TOKEN +# Optional org/repo secret: GH_PAT_WORKFLOWS (PAT with `workflow` scope — +# required if Claude needs to push changes to .github/workflows/*.yml) + name: Claude Code on: @@ -18,7 +36,7 @@ permissions: {} jobs: claude-code: - uses: petry-projects/.github/.github/workflows/claude-code-reusable.yml@main + uses: petry-projects/.github/.github/workflows/claude-code-reusable.yml@v1 secrets: inherit permissions: contents: write diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml index b446bf31..845c3295 100644 --- a/.github/workflows/dependabot-automerge.yml +++ b/.github/workflows/dependabot-automerge.yml @@ -1,20 +1,26 @@ -# Dependabot auto-merge workflow +# ───────────────────────────────────────────────────────────────────────────── +# SOURCE OF TRUTH: petry-projects/.github/standards/workflows/dependabot-automerge.yml +# Standard: petry-projects/.github/standards/dependabot-policy.md +# Reusable: petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml # -# Requires repository secrets: +# AGENTS — READ BEFORE EDITING: +# • This file is a THIN CALLER STUB. All eligibility logic and the GitHub +# App token dance live in the reusable workflow above. +# • You MAY change: nothing in this file in normal use. Adopt verbatim. +# • You MUST NOT change: trigger event (must be `pull_request_target`), +# the `uses:` line, `secrets: inherit`, or the job-level `permissions:` +# block — reusable workflows can be granted no more permissions than the +# calling job has, so removing the stanza breaks the reusable's gh API +# calls. +# • If you need different behaviour, open a PR against the reusable in the +# central repo. +# ───────────────────────────────────────────────────────────────────────────── +# +# Dependabot auto-merge — thin caller for the org-level reusable. +# To adopt: copy this file to .github/workflows/dependabot-automerge.yml in your repo. +# Required org/repo secrets (inherited): # APP_ID — GitHub App ID with contents:write and pull-requests:write # APP_PRIVATE_KEY — GitHub App private key -# -# Auto-approves and enables auto-merge for Dependabot PRs that are: -# - GitHub Actions updates (patch or minor version bumps) -# - Security updates for any ecosystem (patch or minor) -# - Indirect (transitive) dependency updates -# Major version updates are always left for human review. -# Uses --auto so the merge waits for all required CI checks to pass. -# -# Safety model: application ecosystems use open-pull-requests-limit: 0 in -# dependabot.yml, so the only app-ecosystem PRs Dependabot can create are -# security updates. This workflow adds defense-in-depth by also checking -# the package ecosystem. name: Dependabot auto-merge on: @@ -25,54 +31,9 @@ on: permissions: {} jobs: - dependabot: - runs-on: ubuntu-latest + dependabot-automerge: permissions: contents: read pull-requests: read - if: github.event.pull_request.user.login == 'dependabot[bot]' - steps: - - name: Dependabot metadata - id: metadata - uses: dependabot/fetch-metadata@ffa630c65fa7e0ecfa0625b5ceda64399aea1b36 # v2 - with: - github-token: "${{ secrets.GITHUB_TOKEN }}" - skip-commit-verification: true - - - name: Determine if auto-merge eligible - id: eligible - run: | - UPDATE_TYPE="${{ steps.metadata.outputs.update-type }}" - DEP_TYPE="${{ steps.metadata.outputs.dependency-type }}" - ECOSYSTEM="${{ steps.metadata.outputs.package-ecosystem }}" - - # Must be patch, minor, or indirect - if [[ "$UPDATE_TYPE" != "version-update:semver-patch" && \ - "$UPDATE_TYPE" != "version-update:semver-minor" && \ - "$DEP_TYPE" != "indirect" ]]; then - echo "eligible=false" >> "$GITHUB_OUTPUT" - echo "Skipping: major update requires human review" - exit 0 - fi - - # GitHub Actions version updates are always eligible - # App ecosystem PRs can only exist as security updates (limit: 0) - echo "eligible=true" >> "$GITHUB_OUTPUT" - echo "Auto-merge eligible: ecosystem=$ECOSYSTEM update=$UPDATE_TYPE" - - - name: Generate app token - if: steps.eligible.outputs.eligible == 'true' - id: app-token - uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3 - with: - app-id: ${{ secrets.APP_ID }} - private-key: ${{ secrets.APP_PRIVATE_KEY }} - - - name: Approve and enable auto-merge - if: steps.eligible.outputs.eligible == 'true' - run: | - gh pr review --approve "$PR_URL" - gh pr merge --auto --squash "$PR_URL" - env: - PR_URL: ${{ github.event.pull_request.html_url }} - GH_TOKEN: ${{ steps.app-token.outputs.token }} + uses: petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml@v1 + secrets: inherit diff --git a/.github/workflows/dependabot-rebase.yml b/.github/workflows/dependabot-rebase.yml index 46c756f1..b58dbb15 100644 --- a/.github/workflows/dependabot-rebase.yml +++ b/.github/workflows/dependabot-rebase.yml @@ -1,35 +1,26 @@ -# Dependabot update and merge workflow -# Copy to .github/workflows/dependabot-rebase.yml +# ───────────────────────────────────────────────────────────────────────────── +# SOURCE OF TRUTH: petry-projects/.github/standards/workflows/dependabot-rebase.yml +# Standard: petry-projects/.github/standards/dependabot-policy.md +# Reusable: petry-projects/.github/.github/workflows/dependabot-rebase-reusable.yml # -# Requires repository secrets: +# AGENTS — READ BEFORE EDITING: +# • This file is a THIN CALLER STUB. All rebase/merge serialization logic +# lives in the reusable workflow above. +# • You MAY change: nothing in this file in normal use. Adopt verbatim. +# • You MUST NOT change: trigger event, the concurrency group name, +# the `uses:` line, `secrets: inherit`, or the job-level `permissions:` +# block — reusable workflows can be granted no more permissions than the +# calling job has, so removing the stanza breaks the reusable's gh API +# calls. +# • If you need different behaviour, open a PR against the reusable in the +# central repo. +# ───────────────────────────────────────────────────────────────────────────── +# +# Dependabot update-and-merge — thin caller for the org-level reusable. +# To adopt: copy this file to .github/workflows/dependabot-rebase.yml in your repo. +# Required org/repo secrets (inherited): # APP_ID — GitHub App ID with contents:write and pull-requests:write # APP_PRIVATE_KEY — GitHub App private key -# -# Problem: when branch protection requires branches to be up-to-date -# (strict status checks), merging one Dependabot PR makes the others fall -# behind. Dependabot does not auto-rebase PRs that are merely behind — it -# only rebases on its next scheduled run or when there are merge conflicts. -# Additionally, GitHub auto-merge (--auto) may not trigger when rulesets -# cause mergeable_state to report "blocked" even though all requirements -# are actually met. -# -# Solution: after every push to main (typically a merged PR), this workflow: -# 1. Updates behind Dependabot PRs using the merge method (not rebase) -# 2. Merges any Dependabot PR that is up-to-date, approved, and passing CI -# -# Using the app token for merges ensures the resulting push to main triggers -# this workflow again, creating a self-sustaining chain that serializes -# Dependabot PR merges one at a time. -# -# Important: never use the API update-branch endpoint with rebase method on -# Dependabot PRs — it replaces Dependabot's commit signature with GitHub's, -# which breaks dependabot/fetch-metadata verification and causes Dependabot -# to refuse future rebases on that PR. The merge method preserves the -# original commits. -# -# Note: the merge commit is authored by GitHub, not Dependabot, so the -# dependabot-automerge workflow must use skip-commit-verification: true -# in the dependabot/fetch-metadata step. name: Dependabot update and merge on: @@ -44,91 +35,9 @@ concurrency: permissions: {} jobs: - update-and-merge: - runs-on: ubuntu-latest + dependabot-rebase: permissions: contents: read pull-requests: read - steps: - - name: Generate app token - id: app-token - uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3 - with: - app-id: ${{ secrets.APP_ID }} - private-key: ${{ secrets.APP_PRIVATE_KEY }} - - - name: Update and merge Dependabot PRs - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - REPO: ${{ github.repository }} - run: | - # Find open Dependabot PRs - PRS=$(gh pr list --repo "$REPO" --author "app/dependabot" \ - --json number,headRefName \ - --jq '.[] | "\(.number) \(.headRefName)"') - - if [[ -z "$PRS" ]]; then - echo "No open Dependabot PRs" - exit 0 - fi - - MERGED=false - - while IFS=' ' read -r PR_NUMBER HEAD_REF; do - BEHIND=$(gh api "repos/$REPO/compare/main...$HEAD_REF" \ - --jq '.behind_by') - - if [[ "$BEHIND" -gt 0 ]]; then - echo "PR #$PR_NUMBER ($HEAD_REF) is $BEHIND commit(s) behind — merging main into branch" - gh api "repos/$REPO/pulls/$PR_NUMBER/update-branch" \ - -X PUT -f update_method=merge \ - --silent || echo "Warning: failed to update PR #$PR_NUMBER" - continue - fi - - echo "PR #$PR_NUMBER ($HEAD_REF) is up to date — checking if merge-ready" - - # Skip if we already merged one (strict mode means others are now behind) - if [[ "$MERGED" == "true" ]]; then - echo " Skipping — already merged a PR this run" - continue - fi - - # Check if auto-merge is enabled (set by the automerge workflow) - AUTO_MERGE=$(gh pr view "$PR_NUMBER" --repo "$REPO" \ - --json autoMergeRequest --jq '.autoMergeRequest != null') - - if [[ "$AUTO_MERGE" != "true" ]]; then - echo " Skipping — auto-merge not enabled" - continue - fi - - # Check if all required checks pass (look at overall rollup) - CHECKS_PASS=$(gh pr view "$PR_NUMBER" --repo "$REPO" \ - --json statusCheckRollup \ - --jq '[.statusCheckRollup[]? | select(.name != null and .status == "COMPLETED") | .conclusion] | all(. == "SUCCESS" or . == "NEUTRAL" or . == "SKIPPED")') - - CHECKS_PENDING=$(gh pr view "$PR_NUMBER" --repo "$REPO" \ - --json statusCheckRollup \ - --jq '[.statusCheckRollup[]? | select(.name != null and .status != "COMPLETED")] | length') - - if [[ "$CHECKS_PENDING" -gt 0 ]]; then - echo " Skipping — $CHECKS_PENDING check(s) still pending" - continue - fi - - if [[ "$CHECKS_PASS" != "true" ]]; then - echo " Skipping — some checks failed" - continue - fi - - echo " All checks pass — merging PR #$PR_NUMBER" - if gh api "repos/$REPO/pulls/$PR_NUMBER/merge" \ - -X PUT -f merge_method=squash \ - --silent; then - echo " Merged PR #$PR_NUMBER" - MERGED=true - else - echo " Warning: failed to merge PR #$PR_NUMBER" - fi - done <<< "$PRS" + uses: petry-projects/.github/.github/workflows/dependabot-rebase-reusable.yml@v1 + secrets: inherit diff --git a/.github/workflows/dependency-audit.yml b/.github/workflows/dependency-audit.yml index a413603f..f1173774 100644 --- a/.github/workflows/dependency-audit.yml +++ b/.github/workflows/dependency-audit.yml @@ -1,13 +1,22 @@ -# Dependency vulnerability audit -# Copy to .github/workflows/dependency-audit.yml +# ───────────────────────────────────────────────────────────────────────────── +# SOURCE OF TRUTH: petry-projects/.github/standards/workflows/dependency-audit.yml +# Standard: petry-projects/.github/standards/ci-standards.md#5-dependency-audit-dependency-auditym +# Reusable: petry-projects/.github/.github/workflows/dependency-audit-reusable.yml # -# Auto-detects ecosystems present in the repository and runs the appropriate -# audit tool. Fails the build if any dependency has a known security advisory. +# AGENTS — READ BEFORE EDITING: +# • This file is a THIN CALLER STUB. All ecosystem-detection and audit logic +# lives in the reusable workflow above. +# • You MAY change: nothing in this file in normal use. Adopt verbatim. +# • You MUST NOT change: trigger events, the `uses:` line, or job name +# (used as a required status check). +# • If you need different behaviour (new ecosystem, tool version bump), +# open a PR against the reusable in the central repo. +# ───────────────────────────────────────────────────────────────────────────── # -# Add "dependency-audit" as a required status check in branch protection. -# -# Pinned tool versions (update deliberately): -# govulncheck v1.1.4 | cargo-audit 0.22.1 | pip-audit 2.9.0 +# Dependency vulnerability audit — thin caller for the org-level reusable. +# To adopt: copy this file to .github/workflows/dependency-audit.yml in your repo. +# Add "dependency-audit / Detect ecosystems" as a required status check +# in branch protection. name: Dependency audit on: @@ -20,199 +29,5 @@ permissions: contents: read jobs: - detect: - name: Detect ecosystems - runs-on: ubuntu-latest - outputs: - npm: ${{ steps.check.outputs.npm }} - pnpm: ${{ steps.check.outputs.pnpm }} - gomod: ${{ steps.check.outputs.gomod }} - cargo: ${{ steps.check.outputs.cargo }} - pip: ${{ steps.check.outputs.pip }} - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Detect package ecosystems - id: check - run: | - # npm — look for package-lock.json anywhere (excluding node_modules) - if find . -name 'package-lock.json' -not -path '*/node_modules/*' | grep -q .; then - echo "npm=true" >> "$GITHUB_OUTPUT" - else - echo "npm=false" >> "$GITHUB_OUTPUT" - fi - - # pnpm — look for pnpm-lock.yaml anywhere - if find . -name 'pnpm-lock.yaml' -not -path '*/node_modules/*' | grep -q .; then - echo "pnpm=true" >> "$GITHUB_OUTPUT" - else - echo "pnpm=false" >> "$GITHUB_OUTPUT" - fi - - # Go modules — detect via go.mod (not go.sum, which may not exist) - if find . -name 'go.mod' -not -path '*/vendor/*' | grep -q .; then - echo "gomod=true" >> "$GITHUB_OUTPUT" - else - echo "gomod=false" >> "$GITHUB_OUTPUT" - fi - - # Cargo — detect via Cargo.toml anywhere (lockfile may not exist for libraries) - if find . -name 'Cargo.toml' -not -path '*/target/*' | grep -q .; then - echo "cargo=true" >> "$GITHUB_OUTPUT" - else - echo "cargo=false" >> "$GITHUB_OUTPUT" - fi - - # Python — detect pyproject.toml or requirements.txt anywhere - if find . -name 'pyproject.toml' -not -path '*/.venv/*' -not -path '*/venv/*' | grep -q . || \ - find . -name 'requirements.txt' -not -path '*/.venv/*' -not -path '*/venv/*' | grep -q .; then - echo "pip=true" >> "$GITHUB_OUTPUT" - else - echo "pip=false" >> "$GITHUB_OUTPUT" - fi - - audit-npm: - name: npm audit - needs: detect - if: needs.detect.outputs.npm == 'true' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - with: - node-version: "lts/*" - - - name: Audit npm dependencies - run: | - # Audit each package-lock.json found in the repo - status=0 - while IFS= read -r dir; do - echo "::group::npm audit $dir" - if ! (cd "$dir" && npm audit --audit-level=low); then - status=1 - fi - echo "::endgroup::" - done < <(find . -name 'package-lock.json' -not -path '*/node_modules/*' -exec dirname {} \;) - exit $status - - audit-pnpm: - name: pnpm audit - needs: detect - if: needs.detect.outputs.pnpm == 'true' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4 - - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - with: - node-version: "lts/*" - - - name: Audit pnpm dependencies - run: | - # Audit each pnpm-lock.yaml found in the repo - status=0 - while IFS= read -r dir; do - echo "::group::pnpm audit $dir" - if ! (cd "$dir" && pnpm audit --audit-level low); then - status=1 - fi - echo "::endgroup::" - done < <(find . -name 'pnpm-lock.yaml' -not -path '*/node_modules/*' -exec dirname {} \;) - exit $status - - audit-go: - name: govulncheck - needs: detect - if: needs.detect.outputs.gomod == 'true' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v5 - with: - go-version: "stable" - - - name: Install govulncheck - run: go install golang.org/x/vuln/cmd/govulncheck@v1.1.4 - - - name: Audit Go dependencies - run: | - status=0 - while IFS= read -r dir; do - echo "::group::govulncheck $dir" - if ! (cd "$dir" && govulncheck ./...); then - status=1 - fi - echo "::endgroup::" - done < <(find . -name 'go.mod' -not -path '*/vendor/*' -exec dirname {} \;) - exit $status - - audit-cargo: - name: cargo audit - needs: detect - if: needs.detect.outputs.cargo == 'true' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Install Rust stable toolchain - run: rustup toolchain install stable --profile minimal - - - name: Install cargo-audit - run: cargo install cargo-audit@0.22.1 --locked - - - name: Audit Cargo dependencies - run: | - # cargo audit operates on Cargo.lock at workspace root - # For workspaces, a single audit at root covers all crates - status=0 - while IFS= read -r dir; do - echo "::group::cargo audit $dir" - if ! (cd "$dir" && cargo generate-lockfile 2>/dev/null; cargo audit); then - status=1 - fi - echo "::endgroup::" - done < <(find . -name 'Cargo.toml' -not -path '*/target/*' -exec dirname {} \; | sort -u) - exit $status - - audit-pip: - name: pip-audit - needs: detect - if: needs.detect.outputs.pip == 'true' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 - with: - python-version: "3.x" - - - name: Install pip-audit - run: pip install pip-audit==2.9.0 - - - name: Audit Python dependencies - run: | - status=0 - # Audit each Python project found in the repo - while IFS= read -r dir; do - echo "::group::pip-audit $dir" - if [ -f "$dir/pyproject.toml" ]; then - if ! pip-audit "$dir"; then - status=1 - fi - elif [ -f "$dir/requirements.txt" ]; then - if ! pip-audit -r "$dir/requirements.txt"; then - status=1 - fi - fi - echo "::endgroup::" - done < <( - { - find . -name 'pyproject.toml' -not -path '*/.venv/*' -not -path '*/venv/*' -exec dirname {} \; - find . -name 'requirements.txt' -not -path '*/.venv/*' -not -path '*/venv/*' -exec dirname {} \; - } | sort -u - ) - exit $status + dependency-audit: + uses: petry-projects/.github/.github/workflows/dependency-audit-reusable.yml@v1 diff --git a/.github/workflows/feature-ideation.yml b/.github/workflows/feature-ideation.yml index bf643b47..4807fbc1 100644 --- a/.github/workflows/feature-ideation.yml +++ b/.github/workflows/feature-ideation.yml @@ -1,12 +1,36 @@ -# Feature Ideation — markets caller stub. +# ───────────────────────────────────────────────────────────────────────────── +# SOURCE OF TRUTH: petry-projects/.github/standards/workflows/feature-ideation.yml +# Standard: petry-projects/.github/standards/ci-standards.md#8-feature-ideation-feature-ideationyml--bmad-method-repos +# Reusable: petry-projects/.github/.github/workflows/feature-ideation-reusable.yml # -# Calls the org-wide reusable workflow at +# AGENTS — READ BEFORE EDITING: +# • This file is a THIN CALLER STUB. The 5-phase ideation pipeline, the +# Opus 4.6 model selection, the github_token override, and the +# ANTHROPIC_MODEL env var all live in the reusable workflow above. +# • You MAY change: the `project_context` value (the only required edit +# per repo), and optionally the cron schedule. +# • You MUST NOT change: trigger event shape, the `uses:` line, the +# job-level `permissions:` block, or the `secrets:` block — these are +# required for the reusable to work. +# • If you need different behaviour, open a PR against the reusable in +# the central repo. The change will propagate everywhere on next run. +# ───────────────────────────────────────────────────────────────────────────── +# +# Feature Ideation workflow stub — for BMAD Method-enabled repos. +# +# This is a thin caller for the org-wide reusable workflow at # petry-projects/.github/.github/workflows/feature-ideation-reusable.yml # All ideation logic, the multi-skill pipeline, the Opus 4.6 model # selection, and the github_token override live in the reusable workflow. # -# To tune the prompt or pipeline for ALL BMAD repos, edit the reusable -# workflow in petry-projects/.github — changes propagate here on next run. +# To adopt: +# 1. Copy this file to .github/workflows/feature-ideation.yml in your repo. +# 2. Replace the `project_context` value with a 3-5 sentence description +# of your project, its target users, and the competitive landscape Mary +# should research. This is the only required customisation. +# 3. (Optional) Adjust the schedule cron if Friday morning UTC doesn't suit. +# 4. Ensure GitHub Discussions is enabled with an "Ideas" category. +# 5. Confirm the org-level secret CLAUDE_CODE_OAUTH_TOKEN is accessible. # # Standard: https://github.com/petry-projects/.github/blob/main/standards/ci-standards.md#8-feature-ideation-feature-ideationyml--bmad-method-repos name: Feature Research & Ideation (BMAD Analyst) @@ -17,7 +41,7 @@ on: workflow_dispatch: inputs: focus_area: - description: 'Optional focus area (e.g., "vendor onboarding", "real-time updates", "manager UX")' + description: 'Optional focus area (e.g., "accessibility", "performance")' required: false type: string research_depth: @@ -38,37 +62,29 @@ concurrency: jobs: ideate: + # Permissions cascade from the calling job to the reusable workflow. + # The reusable workflow's two jobs (gather-signals + analyze) need: + # - contents: read (checkout, file reads) + # - issues: read (signal collection) + # - pull-requests: read (signal collection) + # - discussions: write (CRITICAL — create/update Discussion threads) + # - id-token: write (claude-code-action OIDC for GitHub App token) permissions: contents: read issues: read pull-requests: read discussions: write id-token: write - uses: petry-projects/.github/.github/workflows/feature-ideation-reusable.yml@main + uses: petry-projects/.github/.github/workflows/feature-ideation-reusable.yml@v1 with: + # === CUSTOMISE THIS PER REPO — the only required edit === + # Replace this paragraph with a 3-5 sentence description of your project, + # its target users, and the competitive landscape. The more specific you + # are, the more useful the proposals. project_context: | - markets is a real-time market operations platform for local farmer - and art markets, serving three sides: market managers (who validate - and publish day-of information), vendors (who report status and - product availability with minimal friction), and customers (who - need accurate market hours, location, and what's on offer before - and during their visit). The product replaces the fragmented - text/email/phone/social-media coordination workflow that managers - and vendors use today. Stack: React + GraphQL + Supabase, mobile- - first PWA-friendly UX. - - Adjacent / competitor space: Instagram and Facebook for ad-hoc - market updates; generic event platforms (Eventbrite); farmers- - market-specific apps (Farmstand, LocalHarvest, Square Market); - vendor management SaaS (Manage My Market, MarketWurks). None - provide a real-time, three-sided coordination layer optimized for - market-day execution. - - Trends to research: real-time object detection / vision models for - vendor inventory capture, low-friction structured reporting for - non-technical vendors, hyperlocal commerce trust signals, - regenerative ag and farm-to-table consumer expectations, vendor - payment/POS integrations, civic/municipal market partnerships. + TODO: Replace this with a description of the project and its market. + Example: "ProjectX is a [type of product] for [target user]. Competitors + include A, B, C. Key emerging trends in this space: X, Y, Z." focus_area: ${{ inputs.focus_area || '' }} research_depth: ${{ inputs.research_depth || 'standard' }} secrets: From 4b6e61b821e546361a2f320de3091f5f6d0ac578 Mon Sep 17 00:00:00 2001 From: DJ Date: Tue, 7 Apr 2026 20:46:59 -0700 Subject: [PATCH 2/4] =?UTF-8?q?chore(workflows):=20drop=20claude.yml=20fro?= =?UTF-8?q?m=20sweep=20=E2=80=94=20handled=20separately?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit claude-code-action self-validates that .github/workflows/claude.yml in a PR is byte-identical to main and refuses to run if it has changed. This blocks PR-driven updates to claude.yml even with admin merge, because branch protection treats the failed claude-code check as a required gate. Keep this sweep PR focused on the other Tier 1 stubs that merge cleanly. claude.yml will be updated via a follow-up direct change. --- .github/workflows/claude.yml | 52 ++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 3faf303c..2ebf06da 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -1,24 +1,3 @@ -# ───────────────────────────────────────────────────────────────────────────── -# SOURCE OF TRUTH: petry-projects/.github/standards/workflows/claude.yml -# Standard: petry-projects/.github/standards/ci-standards.md#4-claude-code-claudeyml -# Reusable: petry-projects/.github/.github/workflows/claude-code-reusable.yml -# -# AGENTS — READ BEFORE EDITING: -# • This file is a THIN CALLER STUB. All Claude Code logic, the prompt, -# allowedTools, and trigger gating live in the reusable workflow above. -# • You MAY change: nothing in this file in normal use. Adopt verbatim. -# • You MUST NOT change: trigger events, job permissions, the `uses:` line, -# or `secrets: inherit`. These are required for the reusable to work. -# • If you need different behaviour, open a PR against the reusable in the -# central repo. The change will propagate everywhere on next run. -# ───────────────────────────────────────────────────────────────────────────── -# -# Claude Code — thin caller that delegates to the org-level reusable workflow. -# To adopt: copy this file to .github/workflows/claude.yml in your repo. -# Required org/repo secret: CLAUDE_CODE_OAUTH_TOKEN -# Optional org/repo secret: GH_PAT_WORKFLOWS (PAT with `workflow` scope — -# required if Claude needs to push changes to .github/workflows/*.yml) - name: Claude Code on: @@ -35,13 +14,34 @@ on: permissions: {} jobs: - claude-code: - uses: petry-projects/.github/.github/workflows/claude-code-reusable.yml@v1 - secrets: inherit + claude: + if: >- + (github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository) || + (github.event_name == 'issue_comment' && github.event.issue.pull_request && + contains(github.event.comment.body, '@claude') && + contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || + (github.event_name == 'pull_request_review_comment' && + contains(github.event.comment.body, '@claude') && + contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || + (github.event_name == 'issues' && github.event.action == 'labeled' && + github.event.label.name == 'claude') + runs-on: ubuntu-latest + timeout-minutes: 60 permissions: + # write required for issue-triggered branch creation contents: write id-token: write pull-requests: write issues: write - actions: read - checks: read + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 1 + - name: Run Claude Code + if: github.event_name != 'pull_request' || github.event.pull_request.user.login != 'dependabot[bot]' + uses: anthropics/claude-code-action@6e2bd52842c65e914eba5c8badd17560bd26b5de # v1.0.89 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + label_trigger: "claude" From 0495e269cc685a0e2f8c178ae08b2b74104dca59 Mon Sep 17 00:00:00 2001 From: DJ Date: Wed, 8 Apr 2026 04:51:52 -0700 Subject: [PATCH 3/4] chore: re-trigger CI after ruleset rename for centralized check names From 26ca793a57412595356dd02f9ff24bd4e447e904 Mon Sep 17 00:00:00 2001 From: DJ Date: Wed, 8 Apr 2026 04:56:07 -0700 Subject: [PATCH 4/4] chore: re-evaluate merge state