From 9bfdbc980a941116da6f46eb5074c1ebdaa55dc2 Mon Sep 17 00:00:00 2001 From: DJ Date: Mon, 6 Apr 2026 18:46:20 -0700 Subject: [PATCH 1/8] feat: add Feature Ideation workflow as a standard for BMAD-enabled repos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Promotes the BMAD Analyst (Mary) feature ideation workflow piloted in petry-projects/TalkTerm to an org-wide standard for any repo with BMAD Method installed. Adds: - standards/workflows/feature-ideation.yml — the canonical template, generalised from TalkTerm. Customisation surface is a single PROJECT_CONTEXT env var that describes the project and its market. - standards/ci-standards.md §8 rewrite — documents the multi-skill ideation pipeline (Market Research → Brainstorming → Party Mode → Adversarial), the Opus 4.6 model requirement, the github_token permissions gotcha, and the show_full_output secrets hazard. - standards/agent-standards.md — adds a "BMAD Method Workflows" section linking the standard from the agent ecosystem docs. The four critical gotchas baked into the template were each discovered empirically during the TalkTerm pilot and would silently regress without the inline comments. Most importantly: the action's auto-generated claude[bot] App token lacks discussions:write, so the workflow MUST pass github_token: ${{ secrets.GITHUB_TOKEN }} explicitly or every Discussion mutation fails silently while the run reports success. Co-Authored-By: Claude Opus 4.6 (1M context) --- standards/agent-standards.md | 13 + standards/ci-standards.md | 83 ++- standards/workflows/feature-ideation.yml | 664 +++++++++++++++++++++++ 3 files changed, 751 insertions(+), 9 deletions(-) create mode 100644 standards/workflows/feature-ideation.yml diff --git a/standards/agent-standards.md b/standards/agent-standards.md index 604a9a17c..3f5cad4d9 100644 --- a/standards/agent-standards.md +++ b/standards/agent-standards.md @@ -92,3 +92,16 @@ on agent configuration files. For repos with `package.json` referencing BMAD modules (e.g., `bmad-method`, `bmad-bgreat-suite`), the `npm` ecosystem already covers version tracking. The AgentShield action adds the agent-specific security layer on top. + +## BMAD Method Workflows + +Repositories with BMAD Method installed (presence of `_bmad/`, `_bmad-output/`, +or equivalent BMAD planning artifacts) MUST include the **Feature Ideation** +workflow, which runs the BMAD Analyst (Mary) on a weekly schedule to research +the market and produce evidence-grounded feature proposals as GitHub Discussions. + +See [CI Standards §8 — Feature Ideation](ci-standards.md#8-feature-ideation-feature-ideationyml--bmad-method-repos) +for the full standard, including the multi-skill ideation pipeline and the +critical configuration gotchas (Opus 4.6 model selection, GitHub token override, +log-secret hygiene). The template is at +[`standards/workflows/feature-ideation.yml`](workflows/feature-ideation.yml). diff --git a/standards/ci-standards.md b/standards/ci-standards.md index 0ac4289f4..777c0412c 100644 --- a/standards/ci-standards.md +++ b/standards/ci-standards.md @@ -13,6 +13,11 @@ and AgentShield workflows are in [`standards/workflows/`](workflows/). The CI, CodeQL, SonarCloud, and Claude Code workflows are documented as patterns below — copy and adapt the examples to each repo's tech stack. +In addition, BMAD Method-enabled repositories MUST also include the conditional +[Feature Ideation workflow](#8-feature-ideation-feature-ideationyml--bmad-method-repos) +documented below — see [`standards/workflows/feature-ideation.yml`](workflows/feature-ideation.yml) +for the template. + ### 1. CI Pipeline (`ci.yml`) The primary build-and-test workflow. Structure varies by tech stack but must include: @@ -302,26 +307,86 @@ These workflows are required only when a specific ecosystem is detected. ### 8. Feature Ideation (`feature-ideation.yml`) — BMAD Method repos -**Condition:** Repository contains a `_bmad/` directory (BMAD Method installed). +**Condition:** Repository has BMAD Method installed (presence of `_bmad/`, +`_bmad-output/`, or equivalent BMAD planning artifacts). + +Scheduled weekly workflow that runs the BMAD Analyst (Mary) on **Claude Opus 4.6** +through a 5-phase multi-skill ideation pipeline, producing evidence-grounded +feature proposals as GitHub Discussions in the **Ideas** category. Each proposal +is a separate Discussion, updated by subsequent runs as the market and project +evolve. + +**The pipeline (the reason this workflow exists):** + +| Phase | Skill | Purpose | +|------:|-------|---------| +| 1 | Load Context | Read signals JSON, planning artifacts, README, codebase extension points | +| 2 | **Market Research** | Iterative evidence gathering — competitor moves, emerging capabilities, user-need signals. Loops until evidence base feels solid. | +| 3 | **Brainstorming** | Divergent ideation — 8-15 raw ideas, builds on Phase 2 evidence. Loops back to research if gaps appear. | +| 4 | **Party Mode** | Collaborative refinement — amplify, connect synergies, ground in feasibility, score on Feasibility/Impact/Urgency. Top 5 advance. | +| 5 | **Adversarial** | 5-question stress test ("So what?", "Who else?", "At what cost?", "What breaks?", "Prove it."). Only survivors are proposed. | +| 6-7 | Publish | Resolve Discussion category, then create new Discussions or comment on existing ones with deltas. | -Scheduled weekly workflow that uses Claude Code Action as the BMAD Analyst -(Mary) to research market trends, analyze project signals, and create per-idea -Discussion threads in the **Ideas** category. Each proposal is a separate -Discussion, updated by subsequent runs as the market and project evolve. +The adversarial pass is the load-bearing part: ideas that survive it are +**robust and defensible**, with a documented rebuttal to the strongest objection. | Setting | Value | |---------|-------| -| **Schedule** | Weekly (recommended: Friday early morning) | -| **Output** | GitHub Discussions in the Ideas category | +| **Model** | `claude-opus-4-6` (set via `ANTHROPIC_MODEL` env var on the step) | +| **Schedule** | Weekly (template uses Friday 07:00 UTC) | +| **Output** | GitHub Discussions in the Ideas category, one per proposal | | **Inputs** | `focus_area` (optional), `research_depth` (quick/standard/deep) | | **Permissions** | `contents: read`, `discussions: write`, `id-token: write` | | **Required secrets** | `CLAUDE_CODE_OAUTH_TOKEN` (org-level) | +| **Typical cost** | ~$2-3 per run on Opus 4.6, standard depth, 25-40 turns | **Prerequisite:** Discussions must be enabled with an "Ideas" category (see [Discussions Configuration](github-settings.md#discussions-configuration)). -See the [TalkTerm implementation](https://github.com/petry-projects/TalkTerm/blob/main/.github/workflows/feature-ideation.yml) -as the reference template. +#### Standard template + +Copy [`workflows/feature-ideation.yml`](workflows/feature-ideation.yml) to +`.github/workflows/feature-ideation.yml` in the target repo. The only +required customisation is the `PROJECT_CONTEXT` env var on the `analyze` +job's Claude step — replace the placeholder with a 3-5 sentence description +of what the project is and what competitive landscape Mary should research. + +#### Critical gotchas + +These were discovered during the TalkTerm pilot and are wired into the template +— **do not remove them without understanding why they exist:** + +1. **Pass `github_token: ${{ secrets.GITHUB_TOKEN }}` explicitly.** + The `claude-code-action` auto-generates its own GitHub App installation + token (`claude[bot]`), which lacks the `discussions: write` scope. + Without an explicit `github_token` input, every `createDiscussion` and + `addDiscussionComment` mutation fails silently with `FORBIDDEN: Resource + not accessible by integration` — the run reports success and produces + no Discussions. Passing the workflow's `GITHUB_TOKEN` makes the job-level + `permissions: discussions: write` grant apply. + +2. **Set `ANTHROPIC_MODEL: claude-opus-4-6` as a step env var.** + The action does not expose model selection as an input — it reads the + `ANTHROPIC_MODEL` environment variable. Opus is required for the depth + the multi-skill pipeline expects; Sonnet runs cheaper but produces + noticeably shallower adversarial passes. + +3. **Do NOT enable `show_full_output: true`.** + It echoes raw tool results to public action logs, which can leak secrets. + The standard template intentionally omits it. + +4. **The ideation pipeline is structured deliberately.** + The Phase 2-5 sequence (Market Research → Brainstorming → Party Mode → + Adversarial) is what produces *defensible* ideas instead of plausible + ones. The phases are explicitly named "skills" so the agent switches + mindsets between them. Keep this structure when customising the prompt. + +#### Reference implementation + +[`petry-projects/TalkTerm`](https://github.com/petry-projects/TalkTerm/blob/main/.github/workflows/feature-ideation.yml) +is the pilot adopter. The TalkTerm workflow is identical to the template +except that `PROJECT_CONTEXT` is replaced with a TalkTerm-specific paragraph +and the codebase scan instructions reference TalkTerm's specific port files. --- diff --git a/standards/workflows/feature-ideation.yml b/standards/workflows/feature-ideation.yml new file mode 100644 index 000000000..1831ae2da --- /dev/null +++ b/standards/workflows/feature-ideation.yml @@ -0,0 +1,664 @@ +# BMAD Analyst (Mary) — Feature Research & Ideation +# +# Weekly automated research and ideation powered by the BMAD Analyst persona. +# Gathers project signals, researches the competitive landscape via web search, +# and produces evidence-grounded feature proposals as GitHub Discussions. +# +# Required for repos that have BMAD Method installed (presence of `_bmad/`, +# `_bmad-output/`, or equivalent BMAD planning artifacts). +# +# How it works (two jobs): +# 1. gather-signals: collects open issues, feature requests, bug patterns, +# merged PRs, releases, and existing Ideas discussions into a JSON payload. +# 2. analyze: runs the BMAD Analyst (Mary) on Opus 4.6 through a 5-phase +# multi-skill pipeline (market research → brainstorming → party mode → +# adversarial → publish), creating one Discussion per surviving idea. +# +# Customisation required when adopting: +# - PROJECT_CONTEXT env var (in the `analyze` job) — short paragraph +# describing what the project is, who it's for, and the competitive +# landscape to research. The agent uses this to steer Phase 2 research. +# - Optional: tighten or relax the schedule cron, the focus_area choices, +# and the timeout_minutes if your project warrants it. +# +# Critical gotchas (do NOT remove without reading the standard): +# - `github_token: ${{ secrets.GITHUB_TOKEN }}` MUST be passed explicitly. +# The action's auto-generated GitHub App installation token (claude[bot]) +# lacks `discussions: write` and silently fails every Discussion mutation. +# Passing GITHUB_TOKEN makes the job-level permissions grant apply. +# - `ANTHROPIC_MODEL: claude-opus-4-6` is set as a step env var. Opus is +# required for the depth of reasoning the multi-skill pipeline expects; +# Sonnet runs cheaper but produces shallower adversarial passes. +# - Do NOT enable `show_full_output: true`. It echoes raw tool results to +# public logs, which can leak secrets. +# +# Standard: https://github.com/petry-projects/.github/blob/main/standards/ci-standards.md#8-feature-ideation-feature-ideationyml--bmad-method-repos +# Reference implementation: https://github.com/petry-projects/TalkTerm/blob/main/.github/workflows/feature-ideation.yml + +name: Feature Research & Ideation (BMAD Analyst) + +on: + schedule: + - cron: '0 7 * * 5' # Friday 07:00 UTC (3 AM EDT / 2 AM EST) + workflow_dispatch: + inputs: + focus_area: + description: 'Optional focus area (e.g., "accessibility", "performance", "developer experience")' + required: false + type: string + research_depth: + description: 'Research depth — controls web search volume and iteration count' + required: false + default: 'standard' + type: choice + options: + - quick + - standard + - deep + +permissions: {} + +concurrency: + group: feature-ideation + cancel-in-progress: false + +jobs: + # --------------------------------------------------------------------------- + # Job 1: Gather signals — issues, discussions, releases, community feedback + # --------------------------------------------------------------------------- + gather-signals: + name: Gather Project Signals + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + issues: read + pull-requests: read + discussions: read + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + outputs: + signals_artifact: ${{ steps.collect.outputs.signals_artifact }} + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 1 + + - name: Collect project signals + id: collect + run: | + set -euo pipefail + REPO="${{ github.repository }}" + SIGNALS_FILE=$(mktemp) + + # --- Open issues (exclude bot-generated, omit body to stay within output limits) --- + OPEN_ISSUES=$(gh issue list --repo "$REPO" --state open --limit 50 \ + --json number,title,labels,createdAt,author \ + -q '[.[] | select(.author.login != "dependabot[bot]" and .author.login != "github-actions[bot]")]' \ + 2>/dev/null || echo '[]') + + # --- Recently closed issues (last 30 days) --- + THIRTY_DAYS_AGO=$(date -u -d '30 days ago' +%Y-%m-%d 2>/dev/null || date -u -v-30d +%Y-%m-%d) + CLOSED_ISSUES=$(gh issue list --repo "$REPO" --state closed --limit 30 \ + --json number,title,labels,closedAt \ + -q "[.[] | select(.closedAt >= \"${THIRTY_DAYS_AGO}\")]" \ + 2>/dev/null || echo '[]') + + # --- Existing Ideas discussions (to detect what's already tracked) --- + # First resolve the Ideas category ID, then query filtered by category. + IDEAS_CAT_ID=$(gh api graphql -f query=' + query($repo: String!, $owner: String!) { + repository(name: $repo, owner: $owner) { + discussionCategories(first: 20) { + nodes { id name } + } + } + }' -f owner="${{ github.repository_owner }}" \ + -f repo="${{ github.event.repository.name }}" \ + --jq '[.data.repository.discussionCategories.nodes[] | select(.name == "Ideas")][0].id' \ + 2>/dev/null || echo "") + + if [ -n "$IDEAS_CAT_ID" ] && [ "$IDEAS_CAT_ID" != "null" ]; then + IDEAS_DISCUSSIONS=$(gh api graphql -f query=' + query($repo: String!, $owner: String!, $categoryId: ID!) { + repository(name: $repo, owner: $owner) { + discussions(first: 100, orderBy: {field: UPDATED_AT, direction: DESC}, + categoryId: $categoryId) { + nodes { + id number title + createdAt updatedAt + labels(first: 10) { nodes { name } } + comments(first: 1) { totalCount } + } + } + } + }' -f owner="${{ github.repository_owner }}" \ + -f repo="${{ github.event.repository.name }}" \ + -f categoryId="$IDEAS_CAT_ID" \ + --jq '.data.repository.discussions.nodes' \ + 2>/dev/null || echo '[]') + else + IDEAS_DISCUSSIONS='[]' + fi + + # --- Recent releases --- + RELEASES=$(gh release list --repo "$REPO" --limit 5 \ + --json tagName,name,publishedAt,isPrerelease \ + 2>/dev/null || echo '[]') + + # --- PR activity (merged last 30 days — what's been shipping) --- + MERGED_PRS=$(gh pr list --repo "$REPO" --state merged --limit 30 \ + --json number,title,labels,mergedAt \ + -q "[.[] | select(.mergedAt >= \"${THIRTY_DAYS_AGO}\")]" \ + 2>/dev/null || echo '[]') + + # --- Feature requests (issues labeled enhancement/feature) --- + FEATURE_REQUESTS=$(echo "$OPEN_ISSUES" | jq -c \ + '[.[] | select(.labels | map(.name) | any(test("enhancement|feature|idea"; "i")))]') + + # --- Bug patterns (open bugs — indicate pain points) --- + BUG_REPORTS=$(echo "$OPEN_ISSUES" | jq -c \ + '[.[] | select(.labels | map(.name) | any(test("bug"; "i")))]') + + # --- Compose signals --- + jq -n \ + --arg scan_date "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ + --arg repo "$REPO" \ + --argjson open_issues "$OPEN_ISSUES" \ + --argjson closed_issues "$CLOSED_ISSUES" \ + --argjson ideas_discussions "$IDEAS_DISCUSSIONS" \ + --argjson releases "$RELEASES" \ + --argjson merged_prs "$MERGED_PRS" \ + --argjson feature_requests "$FEATURE_REQUESTS" \ + --argjson bug_reports "$BUG_REPORTS" \ + '{ + scan_date: $scan_date, + repo: $repo, + open_issues: { count: ($open_issues | length), items: $open_issues }, + closed_issues_30d: { count: ($closed_issues | length), items: $closed_issues }, + ideas_discussions: { count: ($ideas_discussions | length), items: $ideas_discussions }, + releases: $releases, + merged_prs_30d: { count: ($merged_prs | length), items: $merged_prs }, + feature_requests: { count: ($feature_requests | length), items: $feature_requests }, + bug_reports: { count: ($bug_reports | length), items: $bug_reports } + }' > "$SIGNALS_FILE" + + cp "$SIGNALS_FILE" "${{ runner.temp }}/signals.json" + echo "signals_artifact=signals" >> "$GITHUB_OUTPUT" + + # Step summary + { + echo "## Signals Collected" + echo "" + echo "- **Open issues:** $(jq '.open_issues.count' "$SIGNALS_FILE")" + echo "- **Feature requests:** $(jq '.feature_requests.count' "$SIGNALS_FILE")" + echo "- **Bug reports:** $(jq '.bug_reports.count' "$SIGNALS_FILE")" + echo "- **Merged PRs (30d):** $(jq '.merged_prs_30d.count' "$SIGNALS_FILE")" + echo "- **Existing Ideas discussions:** $(jq '.ideas_discussions.count' "$SIGNALS_FILE")" + } >> "$GITHUB_STEP_SUMMARY" + + rm -f "$SIGNALS_FILE" + + - name: Upload signals artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: signals + path: ${{ runner.temp }}/signals.json + retention-days: 7 + + # --------------------------------------------------------------------------- + # Job 2: BMAD Analyst — Research, ideate, and post per-idea Discussions + # --------------------------------------------------------------------------- + analyze: + name: BMAD Analyst — Feature Ideation + needs: [gather-signals] + runs-on: ubuntu-latest + timeout-minutes: 60 + permissions: + contents: read + discussions: write + id-token: write + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 1 + + - name: Download signals artifact + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: signals + path: ${{ runner.temp }} + + - name: Run Claude Code — BMAD Analyst + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + FOCUS_AREA: ${{ inputs.focus_area || '' }} + RESEARCH_DEPTH: ${{ inputs.research_depth || 'standard' }} + # Opus 4.6 is required — see header comment for why. + ANTHROPIC_MODEL: claude-opus-4-6 + # === CUSTOMISE THIS PER REPO === + # Replace this paragraph with a 3-5 sentence description of your project: + # what it is, who it serves, and the competitive landscape Mary should + # research. The more specific you are, the more useful the proposals. + PROJECT_CONTEXT: | + 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." + uses: anthropics/claude-code-action@6e2bd52842c65e914eba5c8badd17560bd26b5de # v1.0.89 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + # CRITICAL: pass the workflow's GITHUB_TOKEN explicitly so the + # job-level `permissions: discussions: write` grant applies. Without + # this, the action uses its own GitHub App token (claude[bot]) which + # lacks discussions:write and silently fails every mutation. + github_token: ${{ secrets.GITHUB_TOKEN }} + claude_args: "--allowedTools Bash,Read,Glob,Grep,WebSearch,WebFetch" + prompt: | + You are **Mary**, the BMAD Strategic Business Analyst, running as an automated + weekly feature research and ideation agent for the **${{ github.repository }}** project. + + ## Thinking & Research Directive + + **Think hard.** Take your time with every phase below. Reason deeply about each + finding before moving on. Do not rush to conclusions — sit with the evidence, + look for non-obvious patterns, and challenge your own assumptions. + + **Research freely — time is no constraint.** You have the full timeout budget to + produce the highest-quality analysis possible. Prefer doing one more web search + over guessing. Prefer reading one more source over assuming. Depth and rigor + matter far more than speed. + + ## Your Persona + + You are a senior analyst with deep expertise in market research, competitive analysis, + and requirements elicitation. You approach every analysis like a treasure hunt — + thrilled by every clue, energized when patterns emerge. You structure insights with + precision while making analysis feel like discovery. You draw upon Porter's Five Forces, + SWOT analysis, and competitive intelligence methodologies naturally. + + ## Project Context + + $PROJECT_CONTEXT + + ## Environment + + - Repository: ${{ github.repository }} + - Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + - Date: $(date -u +%Y-%m-%d) + - Focus area: $FOCUS_AREA (empty = open-ended exploration) + - Research depth: $RESEARCH_DEPTH + + ## Phase 1: Load Context + + 1. Read the project signals collected by the previous job: + ```bash + cat ${{ runner.temp }}/signals.json + ``` + + 2. Discover and read planning artifacts. BMAD installations vary — + use Glob to find them, then Read the ones that exist: + ``` + Glob: _bmad/**/*.md + Glob: _bmad-output/**/*.md + Glob: docs/planning/**/*.md + Glob: docs/architecture/**/*.md + ``` + Prioritise files named like `prd*.md`, `architecture*.md`, + `epics*.md`, `roadmap*.md`, `ux-*.md`, `requirements*.md`. + + 3. Read the project README and any recent changelog or release notes. + + 4. Scan the codebase for extensibility points and underutilized capabilities. + Use Glob and Grep to find: + - Interface/port files (e.g. `**/ports/*.ts`, `**/interfaces/*.go`, + `**/abstract*.py`) — these often hint at planned extensions. + - Adapter / strategy / factory patterns — extension seams. + - TODO / FIXME / HACK comments — known gaps the team has flagged. + - Files named `*config*`, `*settings*` — surfaces user-facing knobs. + + 5. **Read existing Ideas discussions from signals.** These are ideas already being + tracked. You will update them if you have new findings, or leave them alone. + + ## Phase 2: Market Research Skill (Iterative Evidence Gathering) + + **Adopt the "Market Research" mindset.** Your sole job in this phase is to gather + hard evidence — not to generate ideas yet. Be a detective, not a brainstormer. + + Using the **Project Context** above as your starting point, research the + competitive landscape and emerging trends. Cover at minimum: + + ### Competitive Landscape + - What are competitors in this space shipping recently? + - What UX patterns or capabilities are emerging as table stakes? + - Who is winning, who is losing, and why? + - What user segments are underserved? + + ### Emerging Capabilities + - New platform / SDK / model / framework features that unlock new approaches + - Industry trend reports, analyst commentary, conference announcements + - Technology shifts that change what is feasible or affordable + + ### User Need Signals + - Patterns in open issues and feature requests + - Common pain points from bug reports that suggest UX gaps + - What shipped recently (merged PRs) and what gaps remain + - Community discussion themes + + Adjust depth based on RESEARCH_DEPTH: + - **quick**: Skim signals, 1-2 web searches, focus on low-hanging fruit + - **standard**: Thorough signal analysis, 5-8 web searches, balanced exploration + - **deep**: Exhaustive research, 10+ web searches, competitor deep dives, trend analysis + + **Iterate:** After your first research pass, review what you found. Are there gaps? + Promising threads you didn't pull? Do another pass on the most promising areas + before moving on. Keep going until you feel you have a solid evidence base. + + ## Phase 3: Brainstorming Skill (Divergent Ideation) + + **Switch to the "Brainstorming" mindset.** Now generate as many ideas as possible — + quantity over quality. No idea is too wild at this stage. Build on the evidence + from Phase 2 but let your imagination run. + + Generate **8-15 raw feature ideas** across these lenses: + - What gaps do competitors leave that this project could fill? + - What emerging tech could unlock entirely new capabilities? + - What pain points from issues/bugs hint at deeper unmet needs? + - What would make this project indispensable for a specific user persona? + - What would a "10x better" version of an existing feature look like? + + For each raw idea, write a one-line description and note which evidence supports it. + + **Iterate:** Review your list. Can you combine two weaker ideas into one stronger + idea? Did generating ideas reveal a research gap? If so, go back to Phase 2 for + a targeted search, then return here. Repeat until the ideas feel grounded and + diverse. + + ## Phase 4: Party Mode Skill (Collaborative Refinement) + + **Switch to the "Party Mode" mindset.** Imagine you are a room of enthusiastic + product people, engineers, and designers who are all excited about these ideas. + The energy is high. Each person builds on the previous person's suggestion. + + Take the top 5-8 ideas from Phase 3 and refine them collaboratively: + + 1. **Amplify:** For each idea, ask "What if we went even further?" Push the idea + to its most ambitious form. + 2. **Connect:** Look for synergies — can two ideas combine into something greater + than the sum of their parts? + 3. **Ground:** For each amplified idea, ask "What is the minimum version that still + delivers the core value?" Find the sweet spot between ambition and feasibility. + 4. **Score:** For each refined idea, assess: + + | Dimension | Question | + |-----------|----------| + | **Feasibility** | Can we build this with the current stack? How much effort? | + | **Impact** | How many users does this serve? How much value does it add? | + | **Urgency** | Is the market moving here now? Is there competitive pressure? | + + Select the **top 5 ideas** scoring high on at least 2 of 3 dimensions. + + ## Phase 5: Adversarial Skill (Stress-Test & Defend) + + **Switch to the "Adversarial" mindset.** You are now a skeptical VP of Product, + a resource-constrained engineering lead, and a demanding end-user — all at once. + Your job is to tear these ideas apart. Only the ones that survive are worth proposing. + + For each of the top 5 ideas from Phase 4, apply these challenges: + + 1. **"So what?"** — Why would a user actually switch to or stay with this project + because of this? If you can't articulate a compelling user story, cut it. + 2. **"Who else?"** — Is someone else already doing this better, or about to? + If this project can't differentiate, cut it or find the unique angle. + 3. **"At what cost?"** — What is the true engineering cost (not the optimistic + estimate)? What would we have to stop doing to build this? If the opportunity + cost is too high, cut it. + 4. **"What breaks?"** — What existing functionality or architectural assumptions + does this violate? If it creates technical debt or UX inconsistency, can that + be mitigated? + 5. **"Prove it."** — What evidence from Phase 2 actually supports demand for this? + Gut feelings and "it would be cool" are not evidence. Cite specific signals. + + **After the adversarial pass, you should have 3-5 ROBUST and DEFENSIBLE proposals.** + Each surviving idea must have: + - At least one concrete market signal (competitor move, user request, or trend) + - A clear user story explaining why someone would care + - A feasible technical path grounded in the current architecture + - A rebuttal to the strongest objection against it + + If fewer than 3 ideas survive, that is fine — quality over quantity. + If a focus area was specified, weight proposals toward that area but don't + exclude other high-scoring discoveries. + + ## Phase 6: Resolve Discussion Category & Repository IDs + + **Auth note:** All `gh` and `gh api` commands in the phases below use + the `GH_TOKEN` environment variable (already set to the workflow's + `GITHUB_TOKEN`), which has `discussions: write` permission via the + job-level grant. You do NOT need to authenticate manually. + + Before creating or updating Discussions, you need the repository ID and the + "Ideas" discussion category ID. + + ```bash + gh api graphql -f query=' + query($repo: String!, $owner: String!) { + repository(name: $repo, owner: $owner) { + id + discussionCategories(first: 20) { + nodes { id name } + } + } + }' -f owner="${{ github.repository_owner }}" \ + -f repo="${{ github.event.repository.name }}" + ``` + + Save the repository ID and the "Ideas" category ID. If "Ideas" does not exist, + fall back to "General". If neither exists, skip Discussion creation and write + an error to the step summary. + + ## Phase 7: Create or Update Per-Idea Discussions + + For **each** feature proposal surviving Phase 5, check whether a matching Discussion + already exists in the Ideas category. + + ### Matching Logic + + Search existing Ideas discussions (from the signals data) for a title that + matches the proposal's core concept. Use fuzzy matching — the exact title + may differ but the idea should be recognizably the same. + + **Discussion title format:** `💡 ` + + ### If the Discussion DOES NOT exist → Create it + + ```bash + gh api graphql -f query=' + mutation($repoId: ID!, $categoryId: ID!, $title: String!, $body: String!) { + createDiscussion(input: { + repositoryId: $repoId, + categoryId: $categoryId, + title: $title, + body: $body + }) { + discussion { id number url } + } + }' -f repoId="" \ + -f categoryId="" \ + -f title="💡 " \ + -f body="" + ``` + + **New Discussion body format:** + + ```markdown + ## Summary + + <2-3 sentence description of the feature idea> + + ## Market Signal + + + + ## User Signal + + + + ## Technical Opportunity + + + + ## Assessment + + | Dimension | Score | Rationale | + |-----------|-------|-----------| + | Feasibility | high/med/low | ... | + | Impact | high/med/low | ... | + | Urgency | high/med/low | ... | + + ## Adversarial Review + + **Strongest objection:** + **Rebuttal:** + + ## Suggested Next Step + + + + --- + *Proposed by the [BMAD Analyst (Mary)](/${{ github.repository }}/actions/workflows/feature-ideation.yml) on .* + *[Workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})* + ``` + + After creating, add the `enhancement` label to the discussion if your repo + has one. Resolve the label ID with: + ```bash + gh api graphql -f query=' + query($repo: String!, $owner: String!) { + repository(name: $repo, owner: $owner) { + label(name: "enhancement") { id } + } + }' -f owner="${{ github.repository_owner }}" \ + -f repo="${{ github.event.repository.name }}" + ``` + Then apply via `addLabelsToLabelable`. Skip silently if the label doesn't exist. + + ### If the Discussion ALREADY exists → Add an update comment + + When a prior run already created a Discussion for the same idea, add a comment + with new developments rather than creating a duplicate. + + ```bash + gh api graphql -f query=' + mutation($discussionId: ID!, $body: String!) { + addDiscussionComment(input: { + discussionId: $discussionId, + body: $body + }) { + comment { id url } + } + }' -f discussionId="" \ + -f body="" + ``` + + **Update comment format:** + + ```markdown + ## Weekly Update — + + ### What Changed + + + + ### Updated Assessment + + | Dimension | Previous | Current | Delta | + |-----------|----------|---------|-------| + | Feasibility | ... | ... | ↑/↓/→ | + | Impact | ... | ... | ↑/↓/→ | + | Urgency | ... | ... | ↑/↓/→ | + + ### Recommendation + + + + --- + *Updated by the [BMAD Analyst (Mary)](/${{ github.repository }}/actions/workflows/feature-ideation.yml) on .* + *[Workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})* + ``` + + **Important:** Only add an update comment if there is genuinely new information. + If nothing has changed for an existing idea, skip it — don't post "no updates" comments. + + ### If an existing idea should be retired + + If research shows a previously proposed idea is no longer viable (market shifted, + already implemented, superseded by another approach), add a final comment + recommending closure: + + ```markdown + ## Retirement Recommendation — + + **Recommendation:** Close this proposal. + + **Reason:** + +
+ + --- + *Reviewed by the [BMAD Analyst (Mary)](/${{ github.repository }}/actions/workflows/feature-ideation.yml) on .* + ``` + + Do NOT close the Discussion — only recommend closure for human decision. + + ## Phase 8: Step Summary + + Write to $GITHUB_STEP_SUMMARY: + + ```markdown + ## Feature Research & Ideation — + + **Focus:** + **Research depth:** + + ### New Ideas Proposed + | Discussion | Title | Feasibility | Impact | Urgency | + |------------|-------|-------------|--------|---------| + | #N | 💡 ... | ... | ... | ... | + + ### Existing Ideas Updated + | Discussion | Title | Key Change | + |------------|-------|------------| + | #N | 💡 ... | | + + ### Ideas Reviewed — No Update Needed + | Discussion | Title | Reason | + |------------|-------|--------| + | #N | 💡 ... | | + + ### Market & Trend Summary + <2-3 bullet points on the most notable trends this week> + + --- + *[Workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})* + ``` + + ## Rules + + - **Do NOT create issues or PRs.** Output goes to Discussion threads only. + - **Do NOT implement code.** This is research and ideation only. + - **Max 5 new Discussions per run.** Fewer is fine — quality over quantity. + - **Only propose ideas scoring high on 2+ dimensions** (feasibility/impact/urgency). + - **One Discussion per idea.** Never create duplicates — always check existing first. + - **Only comment on existing Discussions when there is new information.** No empty updates. + - **Ground proposals in evidence.** Every proposal must cite at least one market signal, + user signal, or technical opportunity — not just abstract ideas. + - **Be specific about next steps.** Each proposal should suggest a concrete action + (product brief, spike, discussion, etc.) not just "we should consider this." + - **Respect the project's architecture.** Proposals should be compatible with the + patterns documented in AGENTS.md and the planning artifacts. + - **Use the 💡 prefix** in all idea Discussion titles for easy identification. From 6b5778d7e60da0c7e67cb7f5632c65eceb5065e3 Mon Sep 17 00:00:00 2001 From: DJ Date: Mon, 6 Apr 2026 19:01:17 -0700 Subject: [PATCH 2/8] refactor: split feature-ideation into reusable workflow + thin caller stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoids ~600 lines of prompt duplication across every BMAD-enabled repo and makes the multi-skill ideation pipeline tunable in one place — changes here propagate to every adopter on next scheduled run. - .github/workflows/feature-ideation-reusable.yml — the actual reusable workflow (workflow_call). Contains both jobs (signal collection + analyst), the full Phase 1-8 prompt, and the four critical gotchas (Opus 4.6 model, github_token override, no show_full_output, structural Phase 2-5 sequence) hard-coded so they cannot regress. - standards/workflows/feature-ideation.yml — replaced the 600-line copy with a ~60-line caller stub that only defines the schedule, the workflow_dispatch inputs, and a single required parameter: project_context. - standards/ci-standards.md §8 — documents the reusable + caller stub architecture, the inputs/secrets contract, and updated adoption steps. Reference implementation pointer updated to note that TalkTerm is now also a thin caller stub. Inputs exposed by the reusable workflow: - project_context (required) — project description for Mary - focus_area (default '') — typically wired to workflow_dispatch - research_depth (default 'standard') - model (default 'claude-opus-4-6') — escape hatch only - timeout_minutes (default 60) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../workflows/feature-ideation-reusable.yml | 638 +++++++++++++++++ standards/ci-standards.md | 92 ++- standards/workflows/feature-ideation.yml | 664 +----------------- 3 files changed, 734 insertions(+), 660 deletions(-) create mode 100644 .github/workflows/feature-ideation-reusable.yml diff --git a/.github/workflows/feature-ideation-reusable.yml b/.github/workflows/feature-ideation-reusable.yml new file mode 100644 index 000000000..9f4f7f3ec --- /dev/null +++ b/.github/workflows/feature-ideation-reusable.yml @@ -0,0 +1,638 @@ +# Reusable BMAD Analyst (Mary) feature ideation workflow. +# +# Single source of truth for the org. Repo-level feature-ideation.yml files +# are thin caller stubs that schedule this workflow with their own +# project_context — see standards/workflows/feature-ideation.yml for the +# template stub and ci-standards.md §8 for the full standard. +# +# Why a reusable workflow: +# - The 5-phase ideation pipeline (Market Research → Brainstorming → Party +# Mode → Adversarial → Publish) is universal. Tuning the pattern in one +# place propagates to every BMAD-enabled repo on next run. +# - The two critical gotchas (github_token override, ANTHROPIC_MODEL env var) +# are baked in here so callers cannot accidentally regress them. +# +# Standard: https://github.com/petry-projects/.github/blob/main/standards/ci-standards.md#8-feature-ideation-feature-ideationyml--bmad-method-repos +name: Feature Ideation (Reusable) + +on: + workflow_call: + inputs: + project_context: + description: | + 3-5 sentence description of the project, its target users, and the + competitive landscape Mary should research. The more specific you + are, the more useful the proposals. This is the only required input. + required: true + type: string + focus_area: + description: 'Optional focus area to steer research (e.g., "accessibility", "performance")' + required: false + default: '' + type: string + research_depth: + description: 'Research depth: quick | standard | deep' + required: false + default: 'standard' + type: string + model: + description: 'Anthropic model ID. Opus 4.6 is the default and recommended — Sonnet produces shallower adversarial passes.' + required: false + default: 'claude-opus-4-6' + type: string + timeout_minutes: + description: 'Timeout for the analyst job (the gather-signals job has its own short timeout).' + required: false + default: 60 + type: number + secrets: + CLAUDE_CODE_OAUTH_TOKEN: + description: 'Claude Code OAuth token (org-level secret)' + required: true + +jobs: + # --------------------------------------------------------------------------- + # Job 1: Gather signals — issues, discussions, releases, community feedback + # --------------------------------------------------------------------------- + gather-signals: + name: Gather Project Signals + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + issues: read + pull-requests: read + discussions: read + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 1 + + - name: Collect project signals + run: | + set -euo pipefail + REPO="${{ github.repository }}" + SIGNALS_FILE=$(mktemp) + + # --- Open issues (exclude bot-generated, omit body to stay within output limits) --- + OPEN_ISSUES=$(gh issue list --repo "$REPO" --state open --limit 50 \ + --json number,title,labels,createdAt,author \ + -q '[.[] | select(.author.login != "dependabot[bot]" and .author.login != "github-actions[bot]")]' \ + 2>/dev/null || echo '[]') + + # --- Recently closed issues (last 30 days) --- + THIRTY_DAYS_AGO=$(date -u -d '30 days ago' +%Y-%m-%d 2>/dev/null || date -u -v-30d +%Y-%m-%d) + CLOSED_ISSUES=$(gh issue list --repo "$REPO" --state closed --limit 30 \ + --json number,title,labels,closedAt \ + -q "[.[] | select(.closedAt >= \"${THIRTY_DAYS_AGO}\")]" \ + 2>/dev/null || echo '[]') + + # --- Existing Ideas discussions (to detect what's already tracked) --- + IDEAS_CAT_ID=$(gh api graphql -f query=' + query($repo: String!, $owner: String!) { + repository(name: $repo, owner: $owner) { + discussionCategories(first: 20) { + nodes { id name } + } + } + }' -f owner="${{ github.repository_owner }}" \ + -f repo="${{ github.event.repository.name }}" \ + --jq '[.data.repository.discussionCategories.nodes[] | select(.name == "Ideas")][0].id' \ + 2>/dev/null || echo "") + + if [ -n "$IDEAS_CAT_ID" ] && [ "$IDEAS_CAT_ID" != "null" ]; then + IDEAS_DISCUSSIONS=$(gh api graphql -f query=' + query($repo: String!, $owner: String!, $categoryId: ID!) { + repository(name: $repo, owner: $owner) { + discussions(first: 100, orderBy: {field: UPDATED_AT, direction: DESC}, + categoryId: $categoryId) { + nodes { + id number title + createdAt updatedAt + labels(first: 10) { nodes { name } } + comments(first: 1) { totalCount } + } + } + } + }' -f owner="${{ github.repository_owner }}" \ + -f repo="${{ github.event.repository.name }}" \ + -f categoryId="$IDEAS_CAT_ID" \ + --jq '.data.repository.discussions.nodes' \ + 2>/dev/null || echo '[]') + else + IDEAS_DISCUSSIONS='[]' + fi + + # --- Recent releases --- + RELEASES=$(gh release list --repo "$REPO" --limit 5 \ + --json tagName,name,publishedAt,isPrerelease \ + 2>/dev/null || echo '[]') + + # --- PR activity (merged last 30 days) --- + MERGED_PRS=$(gh pr list --repo "$REPO" --state merged --limit 30 \ + --json number,title,labels,mergedAt \ + -q "[.[] | select(.mergedAt >= \"${THIRTY_DAYS_AGO}\")]" \ + 2>/dev/null || echo '[]') + + # --- Feature requests / bug reports --- + FEATURE_REQUESTS=$(echo "$OPEN_ISSUES" | jq -c \ + '[.[] | select(.labels | map(.name) | any(test("enhancement|feature|idea"; "i")))]') + BUG_REPORTS=$(echo "$OPEN_ISSUES" | jq -c \ + '[.[] | select(.labels | map(.name) | any(test("bug"; "i")))]') + + jq -n \ + --arg scan_date "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ + --arg repo "$REPO" \ + --argjson open_issues "$OPEN_ISSUES" \ + --argjson closed_issues "$CLOSED_ISSUES" \ + --argjson ideas_discussions "$IDEAS_DISCUSSIONS" \ + --argjson releases "$RELEASES" \ + --argjson merged_prs "$MERGED_PRS" \ + --argjson feature_requests "$FEATURE_REQUESTS" \ + --argjson bug_reports "$BUG_REPORTS" \ + '{ + scan_date: $scan_date, + repo: $repo, + open_issues: { count: ($open_issues | length), items: $open_issues }, + closed_issues_30d: { count: ($closed_issues | length), items: $closed_issues }, + ideas_discussions: { count: ($ideas_discussions | length), items: $ideas_discussions }, + releases: $releases, + merged_prs_30d: { count: ($merged_prs | length), items: $merged_prs }, + feature_requests: { count: ($feature_requests | length), items: $feature_requests }, + bug_reports: { count: ($bug_reports | length), items: $bug_reports } + }' > "$SIGNALS_FILE" + + cp "$SIGNALS_FILE" "${{ runner.temp }}/signals.json" + + { + echo "## Signals Collected" + echo "" + echo "- **Open issues:** $(jq '.open_issues.count' "$SIGNALS_FILE")" + echo "- **Feature requests:** $(jq '.feature_requests.count' "$SIGNALS_FILE")" + echo "- **Bug reports:** $(jq '.bug_reports.count' "$SIGNALS_FILE")" + echo "- **Merged PRs (30d):** $(jq '.merged_prs_30d.count' "$SIGNALS_FILE")" + echo "- **Existing Ideas discussions:** $(jq '.ideas_discussions.count' "$SIGNALS_FILE")" + } >> "$GITHUB_STEP_SUMMARY" + + rm -f "$SIGNALS_FILE" + + - name: Upload signals artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: signals + path: ${{ runner.temp }}/signals.json + retention-days: 7 + + # --------------------------------------------------------------------------- + # Job 2: BMAD Analyst — Research, ideate, and post per-idea Discussions + # --------------------------------------------------------------------------- + analyze: + name: BMAD Analyst — Feature Ideation + needs: [gather-signals] + runs-on: ubuntu-latest + timeout-minutes: ${{ inputs.timeout_minutes }} + permissions: + contents: read + discussions: write + id-token: write + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 1 + + - name: Download signals artifact + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: signals + path: ${{ runner.temp }} + + - name: Run Claude Code — BMAD Analyst + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + FOCUS_AREA: ${{ inputs.focus_area }} + RESEARCH_DEPTH: ${{ inputs.research_depth }} + PROJECT_CONTEXT: ${{ inputs.project_context }} + # CRITICAL: Opus 4.6 is required for the depth the multi-skill + # pipeline expects. The action does not expose model selection as + # an input — it reads ANTHROPIC_MODEL from the environment. + ANTHROPIC_MODEL: ${{ inputs.model }} + uses: anthropics/claude-code-action@6e2bd52842c65e914eba5c8badd17560bd26b5de # v1.0.89 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + # CRITICAL: pass the workflow's GITHUB_TOKEN explicitly so the + # job-level `permissions: discussions: write` grant applies. Without + # this, the action uses its own GitHub App token (claude[bot]) which + # lacks discussions:write and silently fails every mutation. + github_token: ${{ secrets.GITHUB_TOKEN }} + claude_args: "--allowedTools Bash,Read,Glob,Grep,WebSearch,WebFetch" + prompt: | + You are **Mary**, the BMAD Strategic Business Analyst, running as an automated + weekly feature research and ideation agent for the **${{ github.repository }}** project. + + ## Thinking & Research Directive + + **Think hard.** Take your time with every phase below. Reason deeply about each + finding before moving on. Do not rush to conclusions — sit with the evidence, + look for non-obvious patterns, and challenge your own assumptions. + + **Research freely — time is no constraint.** You have the full timeout budget to + produce the highest-quality analysis possible. Prefer doing one more web search + over guessing. Prefer reading one more source over assuming. Depth and rigor + matter far more than speed. + + ## Your Persona + + You are a senior analyst with deep expertise in market research, competitive analysis, + and requirements elicitation. You approach every analysis like a treasure hunt — + thrilled by every clue, energized when patterns emerge. You structure insights with + precision while making analysis feel like discovery. You draw upon Porter's Five Forces, + SWOT analysis, and competitive intelligence methodologies naturally. + + ## Project Context + + $PROJECT_CONTEXT + + ## Environment + + - Repository: ${{ github.repository }} + - Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + - Date: $(date -u +%Y-%m-%d) + - Focus area: $FOCUS_AREA (empty = open-ended exploration) + - Research depth: $RESEARCH_DEPTH + + ## Phase 1: Load Context + + 1. Read the project signals collected by the previous job: + ```bash + cat ${{ runner.temp }}/signals.json + ``` + + 2. Discover and read planning artifacts. BMAD installations vary — + use Glob to find them, then Read the ones that exist: + ``` + Glob: _bmad/**/*.md + Glob: _bmad-output/**/*.md + Glob: docs/planning/**/*.md + Glob: docs/architecture/**/*.md + ``` + Prioritise files named like `prd*.md`, `architecture*.md`, + `epics*.md`, `roadmap*.md`, `ux-*.md`, `requirements*.md`. + + 3. Read the project README and any recent changelog or release notes. + + 4. Scan the codebase for extensibility points and underutilized capabilities. + Use Glob and Grep to find: + - Interface/port files (e.g. `**/ports/*.ts`, `**/interfaces/*.go`, + `**/abstract*.py`) — these often hint at planned extensions. + - Adapter / strategy / factory patterns — extension seams. + - TODO / FIXME / HACK comments — known gaps the team has flagged. + - Files named `*config*`, `*settings*` — surfaces user-facing knobs. + + 5. **Read existing Ideas discussions from signals.** These are ideas already being + tracked. You will update them if you have new findings, or leave them alone. + + ## Phase 2: Market Research Skill (Iterative Evidence Gathering) + + **Adopt the "Market Research" mindset.** Your sole job in this phase is to gather + hard evidence — not to generate ideas yet. Be a detective, not a brainstormer. + + Using the **Project Context** above as your starting point, research the + competitive landscape and emerging trends. Cover at minimum: + + ### Competitive Landscape + - What are competitors in this space shipping recently? + - What UX patterns or capabilities are emerging as table stakes? + - Who is winning, who is losing, and why? + - What user segments are underserved? + + ### Emerging Capabilities + - New platform / SDK / model / framework features that unlock new approaches + - Industry trend reports, analyst commentary, conference announcements + - Technology shifts that change what is feasible or affordable + + ### User Need Signals + - Patterns in open issues and feature requests + - Common pain points from bug reports that suggest UX gaps + - What shipped recently (merged PRs) and what gaps remain + - Community discussion themes + + Adjust depth based on RESEARCH_DEPTH: + - **quick**: Skim signals, 1-2 web searches, focus on low-hanging fruit + - **standard**: Thorough signal analysis, 5-8 web searches, balanced exploration + - **deep**: Exhaustive research, 10+ web searches, competitor deep dives, trend analysis + + **Iterate:** After your first research pass, review what you found. Are there gaps? + Promising threads you didn't pull? Do another pass on the most promising areas + before moving on. Keep going until you feel you have a solid evidence base. + + ## Phase 3: Brainstorming Skill (Divergent Ideation) + + **Switch to the "Brainstorming" mindset.** Now generate as many ideas as possible — + quantity over quality. No idea is too wild at this stage. Build on the evidence + from Phase 2 but let your imagination run. + + Generate **8-15 raw feature ideas** across these lenses: + - What gaps do competitors leave that this project could fill? + - What emerging tech could unlock entirely new capabilities? + - What pain points from issues/bugs hint at deeper unmet needs? + - What would make this project indispensable for a specific user persona? + - What would a "10x better" version of an existing feature look like? + + For each raw idea, write a one-line description and note which evidence supports it. + + **Iterate:** Review your list. Can you combine two weaker ideas into one stronger + idea? Did generating ideas reveal a research gap? If so, go back to Phase 2 for + a targeted search, then return here. Repeat until the ideas feel grounded and + diverse. + + ## Phase 4: Party Mode Skill (Collaborative Refinement) + + **Switch to the "Party Mode" mindset.** Imagine you are a room of enthusiastic + product people, engineers, and designers who are all excited about these ideas. + The energy is high. Each person builds on the previous person's suggestion. + + Take the top 5-8 ideas from Phase 3 and refine them collaboratively: + + 1. **Amplify:** For each idea, ask "What if we went even further?" Push the idea + to its most ambitious form. + 2. **Connect:** Look for synergies — can two ideas combine into something greater + than the sum of their parts? + 3. **Ground:** For each amplified idea, ask "What is the minimum version that still + delivers the core value?" Find the sweet spot between ambition and feasibility. + 4. **Score:** For each refined idea, assess: + + | Dimension | Question | + |-----------|----------| + | **Feasibility** | Can we build this with the current stack? How much effort? | + | **Impact** | How many users does this serve? How much value does it add? | + | **Urgency** | Is the market moving here now? Is there competitive pressure? | + + Select the **top 5 ideas** scoring high on at least 2 of 3 dimensions. + + ## Phase 5: Adversarial Skill (Stress-Test & Defend) + + **Switch to the "Adversarial" mindset.** You are now a skeptical VP of Product, + a resource-constrained engineering lead, and a demanding end-user — all at once. + Your job is to tear these ideas apart. Only the ones that survive are worth proposing. + + For each of the top 5 ideas from Phase 4, apply these challenges: + + 1. **"So what?"** — Why would a user actually switch to or stay with this project + because of this? If you can't articulate a compelling user story, cut it. + 2. **"Who else?"** — Is someone else already doing this better, or about to? + If this project can't differentiate, cut it or find the unique angle. + 3. **"At what cost?"** — What is the true engineering cost (not the optimistic + estimate)? What would we have to stop doing to build this? If the opportunity + cost is too high, cut it. + 4. **"What breaks?"** — What existing functionality or architectural assumptions + does this violate? If it creates technical debt or UX inconsistency, can that + be mitigated? + 5. **"Prove it."** — What evidence from Phase 2 actually supports demand for this? + Gut feelings and "it would be cool" are not evidence. Cite specific signals. + + **After the adversarial pass, you should have 3-5 ROBUST and DEFENSIBLE proposals.** + Each surviving idea must have: + - At least one concrete market signal (competitor move, user request, or trend) + - A clear user story explaining why someone would care + - A feasible technical path grounded in the current architecture + - A rebuttal to the strongest objection against it + + If fewer than 3 ideas survive, that is fine — quality over quantity. + If a focus area was specified, weight proposals toward that area but don't + exclude other high-scoring discoveries. + + ## Phase 6: Resolve Discussion Category & Repository IDs + + **Auth note:** All `gh` and `gh api` commands in the phases below use + the `GH_TOKEN` environment variable (already set to the workflow's + `GITHUB_TOKEN`), which has `discussions: write` permission via the + job-level grant. You do NOT need to authenticate manually. + + Before creating or updating Discussions, you need the repository ID and the + "Ideas" discussion category ID. + + ```bash + gh api graphql -f query=' + query($repo: String!, $owner: String!) { + repository(name: $repo, owner: $owner) { + id + discussionCategories(first: 20) { + nodes { id name } + } + } + }' -f owner="${{ github.repository_owner }}" \ + -f repo="${{ github.event.repository.name }}" + ``` + + Save the repository ID and the "Ideas" category ID. If "Ideas" does not exist, + fall back to "General". If neither exists, skip Discussion creation and write + an error to the step summary. + + ## Phase 7: Create or Update Per-Idea Discussions + + For **each** feature proposal surviving Phase 5, check whether a matching Discussion + already exists in the Ideas category. + + ### Matching Logic + + Search existing Ideas discussions (from the signals data) for a title that + matches the proposal's core concept. Use fuzzy matching — the exact title + may differ but the idea should be recognizably the same. + + **Discussion title format:** `💡 ` + + ### If the Discussion DOES NOT exist → Create it + + ```bash + gh api graphql -f query=' + mutation($repoId: ID!, $categoryId: ID!, $title: String!, $body: String!) { + createDiscussion(input: { + repositoryId: $repoId, + categoryId: $categoryId, + title: $title, + body: $body + }) { + discussion { id number url } + } + }' -f repoId="" \ + -f categoryId="" \ + -f title="💡 " \ + -f body="" + ``` + + **New Discussion body format:** + + ```markdown + ## Summary + + <2-3 sentence description of the feature idea> + + ## Market Signal + + + + ## User Signal + + + + ## Technical Opportunity + + + + ## Assessment + + | Dimension | Score | Rationale | + |-----------|-------|-----------| + | Feasibility | high/med/low | ... | + | Impact | high/med/low | ... | + | Urgency | high/med/low | ... | + + ## Adversarial Review + + **Strongest objection:** + **Rebuttal:** + + ## Suggested Next Step + + + + --- + *Proposed by the [BMAD Analyst (Mary)](/${{ github.repository }}/actions/workflows/feature-ideation.yml) on .* + *[Workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})* + ``` + + After creating, add the `enhancement` label to the discussion if your repo + has one. Resolve the label ID with: + ```bash + gh api graphql -f query=' + query($repo: String!, $owner: String!) { + repository(name: $repo, owner: $owner) { + label(name: "enhancement") { id } + } + }' -f owner="${{ github.repository_owner }}" \ + -f repo="${{ github.event.repository.name }}" + ``` + Then apply via `addLabelsToLabelable`. Skip silently if the label doesn't exist. + + ### If the Discussion ALREADY exists → Add an update comment + + When a prior run already created a Discussion for the same idea, add a comment + with new developments rather than creating a duplicate. + + ```bash + gh api graphql -f query=' + mutation($discussionId: ID!, $body: String!) { + addDiscussionComment(input: { + discussionId: $discussionId, + body: $body + }) { + comment { id url } + } + }' -f discussionId="" \ + -f body="" + ``` + + **Update comment format:** + + ```markdown + ## Weekly Update — + + ### What Changed + + + + ### Updated Assessment + + | Dimension | Previous | Current | Delta | + |-----------|----------|---------|-------| + | Feasibility | ... | ... | ↑/↓/→ | + | Impact | ... | ... | ↑/↓/→ | + | Urgency | ... | ... | ↑/↓/→ | + + ### Recommendation + + + + --- + *Updated by the [BMAD Analyst (Mary)](/${{ github.repository }}/actions/workflows/feature-ideation.yml) on .* + *[Workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})* + ``` + + **Important:** Only add an update comment if there is genuinely new information. + If nothing has changed for an existing idea, skip it — don't post "no updates" comments. + + ### If an existing idea should be retired + + If research shows a previously proposed idea is no longer viable (market shifted, + already implemented, superseded by another approach), add a final comment + recommending closure: + + ```markdown + ## Retirement Recommendation — + + **Recommendation:** Close this proposal. + + **Reason:** + +
+ + --- + *Reviewed by the [BMAD Analyst (Mary)](/${{ github.repository }}/actions/workflows/feature-ideation.yml) on .* + ``` + + Do NOT close the Discussion — only recommend closure for human decision. + + ## Phase 8: Step Summary + + Write to $GITHUB_STEP_SUMMARY: + + ```markdown + ## Feature Research & Ideation — + + **Focus:** + **Research depth:** + + ### New Ideas Proposed + | Discussion | Title | Feasibility | Impact | Urgency | + |------------|-------|-------------|--------|---------| + | #N | 💡 ... | ... | ... | ... | + + ### Existing Ideas Updated + | Discussion | Title | Key Change | + |------------|-------|------------| + | #N | 💡 ... | | + + ### Ideas Reviewed — No Update Needed + | Discussion | Title | Reason | + |------------|-------|--------| + | #N | 💡 ... | | + + ### Market & Trend Summary + <2-3 bullet points on the most notable trends this week> + + --- + *[Workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})* + ``` + + ## Rules + + - **Do NOT create issues or PRs.** Output goes to Discussion threads only. + - **Do NOT implement code.** This is research and ideation only. + - **Max 5 new Discussions per run.** Fewer is fine — quality over quantity. + - **Only propose ideas scoring high on 2+ dimensions** (feasibility/impact/urgency). + - **One Discussion per idea.** Never create duplicates — always check existing first. + - **Only comment on existing Discussions when there is new information.** No empty updates. + - **Ground proposals in evidence.** Every proposal must cite at least one market signal, + user signal, or technical opportunity — not just abstract ideas. + - **Be specific about next steps.** Each proposal should suggest a concrete action + (product brief, spike, discussion, etc.) not just "we should consider this." + - **Respect the project's architecture.** Proposals should be compatible with the + patterns documented in AGENTS.md and the planning artifacts. + - **Use the 💡 prefix** in all idea Discussion titles for easy identification. diff --git a/standards/ci-standards.md b/standards/ci-standards.md index 777c0412c..7d46fbfea 100644 --- a/standards/ci-standards.md +++ b/standards/ci-standards.md @@ -343,20 +343,46 @@ The adversarial pass is the load-bearing part: ideas that survive it are **Prerequisite:** Discussions must be enabled with an "Ideas" category (see [Discussions Configuration](github-settings.md#discussions-configuration)). -#### Standard template - -Copy [`workflows/feature-ideation.yml`](workflows/feature-ideation.yml) to -`.github/workflows/feature-ideation.yml` in the target repo. The only -required customisation is the `PROJECT_CONTEXT` env var on the `analyze` -job's Claude step — replace the placeholder with a 3-5 sentence description -of what the project is and what competitive landscape Mary should research. - -#### Critical gotchas - -These were discovered during the TalkTerm pilot and are wired into the template -— **do not remove them without understanding why they exist:** - -1. **Pass `github_token: ${{ secrets.GITHUB_TOKEN }}` explicitly.** +#### Architecture: reusable workflow + thin caller stub + +To avoid duplicating ~600 lines of prompt logic across every BMAD repo — +and to let us tune the multi-skill pipeline in one place — the workflow is +split into two parts: + +1. **Reusable workflow** (single source of truth, hosted in this repo): + [`.github/workflows/feature-ideation-reusable.yml`](../.github/workflows/feature-ideation-reusable.yml). + Contains both jobs (signal collection + analyst), the full prompt with + the 5-phase pipeline, and the four critical gotchas (model selection, + token override, etc.) hard-coded so they cannot regress. + +2. **Caller stub** (copied into each adopting repo, ~60 lines): + [`standards/workflows/feature-ideation.yml`](workflows/feature-ideation.yml). + Defines the schedule, the `workflow_dispatch` inputs, and calls the + reusable workflow with a single required parameter: `project_context`. + +When we tune the prompt, the model, or the gotchas, we change one file in +this repo and every adopter picks up the change on their next scheduled run. + +#### Adopting in a new repo + +1. Copy [`standards/workflows/feature-ideation.yml`](workflows/feature-ideation.yml) + to `.github/workflows/feature-ideation.yml` in the target repo. +2. Replace the `project_context` value with a 3-5 sentence description of + what the project is, who it serves, and the competitive landscape Mary + should research. This is the **only** required edit. +3. (Optional) Adjust the cron schedule, focus area choices, or pin to a + tag instead of `@main` if you want change isolation. +4. Ensure GitHub Discussions is enabled with an "Ideas" category — see + [Discussions Configuration](github-settings.md#discussions-configuration). +5. Confirm the org-level secret `CLAUDE_CODE_OAUTH_TOKEN` is accessible. + +#### Critical gotchas (baked into the reusable workflow) + +These were discovered during the TalkTerm pilot. They live in the reusable +workflow with inline warning comments — **do not remove them without +understanding why they exist:** + +1. **`github_token: ${{ secrets.GITHUB_TOKEN }}` is passed explicitly.** The `claude-code-action` auto-generates its own GitHub App installation token (`claude[bot]`), which lacks the `discussions: write` scope. Without an explicit `github_token` input, every `createDiscussion` and @@ -365,28 +391,42 @@ These were discovered during the TalkTerm pilot and are wired into the template no Discussions. Passing the workflow's `GITHUB_TOKEN` makes the job-level `permissions: discussions: write` grant apply. -2. **Set `ANTHROPIC_MODEL: claude-opus-4-6` as a step env var.** +2. **`ANTHROPIC_MODEL: claude-opus-4-6` is set as a step env var.** The action does not expose model selection as an input — it reads the `ANTHROPIC_MODEL` environment variable. Opus is required for the depth the multi-skill pipeline expects; Sonnet runs cheaper but produces - noticeably shallower adversarial passes. + noticeably shallower adversarial passes. The reusable workflow exposes + this as the optional `model` input for callers that need an override. -3. **Do NOT enable `show_full_output: true`.** +3. **`show_full_output: true` is NOT enabled.** It echoes raw tool results to public action logs, which can leak secrets. - The standard template intentionally omits it. + The reusable workflow intentionally omits it. + +4. **The Phase 2-5 sequence is structural, not cosmetic.** + Each phase explicitly switches the agent's mindset ("skill"), which is + what produces *defensible* ideas instead of plausible ones. Keep this + structure when tuning the prompt. + +#### Reusable workflow inputs + +| Input | Required | Default | Notes | +|-------|----------|---------|-------| +| `project_context` | yes | — | 3-5 sentence project description; the only required input | +| `focus_area` | no | `''` | Optional research focus, typically wired to `workflow_dispatch` input | +| `research_depth` | no | `'standard'` | `quick` / `standard` / `deep` | +| `model` | no | `'claude-opus-4-6'` | Override only for cost experiments — see gotcha #2 | +| `timeout_minutes` | no | `60` | Analyst job timeout (signal collection has its own short timeout) | -4. **The ideation pipeline is structured deliberately.** - The Phase 2-5 sequence (Market Research → Brainstorming → Party Mode → - Adversarial) is what produces *defensible* ideas instead of plausible - ones. The phases are explicitly named "skills" so the agent switches - mindsets between them. Keep this structure when customising the prompt. +| Secret | Required | Notes | +|--------|----------|-------| +| `CLAUDE_CODE_OAUTH_TOKEN` | yes | Org-level secret, must be passed explicitly by the caller | #### Reference implementation [`petry-projects/TalkTerm`](https://github.com/petry-projects/TalkTerm/blob/main/.github/workflows/feature-ideation.yml) -is the pilot adopter. The TalkTerm workflow is identical to the template -except that `PROJECT_CONTEXT` is replaced with a TalkTerm-specific paragraph -and the codebase scan instructions reference TalkTerm's specific port files. +is the pilot adopter. The TalkTerm workflow is the standard caller stub +with `project_context` set to a TalkTerm-specific paragraph — no other +customisation. --- diff --git a/standards/workflows/feature-ideation.yml b/standards/workflows/feature-ideation.yml index 1831ae2da..471ac848c 100644 --- a/standards/workflows/feature-ideation.yml +++ b/standards/workflows/feature-ideation.yml @@ -1,40 +1,20 @@ -# BMAD Analyst (Mary) — Feature Research & Ideation +# Feature Ideation workflow stub — for BMAD Method-enabled repos. # -# Weekly automated research and ideation powered by the BMAD Analyst persona. -# Gathers project signals, researches the competitive landscape via web search, -# and produces evidence-grounded feature proposals as GitHub Discussions. +# 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. # -# Required for repos that have BMAD Method installed (presence of `_bmad/`, -# `_bmad-output/`, or equivalent BMAD planning artifacts). -# -# How it works (two jobs): -# 1. gather-signals: collects open issues, feature requests, bug patterns, -# merged PRs, releases, and existing Ideas discussions into a JSON payload. -# 2. analyze: runs the BMAD Analyst (Mary) on Opus 4.6 through a 5-phase -# multi-skill pipeline (market research → brainstorming → party mode → -# adversarial → publish), creating one Discussion per surviving idea. -# -# Customisation required when adopting: -# - PROJECT_CONTEXT env var (in the `analyze` job) — short paragraph -# describing what the project is, who it's for, and the competitive -# landscape to research. The agent uses this to steer Phase 2 research. -# - Optional: tighten or relax the schedule cron, the focus_area choices, -# and the timeout_minutes if your project warrants it. -# -# Critical gotchas (do NOT remove without reading the standard): -# - `github_token: ${{ secrets.GITHUB_TOKEN }}` MUST be passed explicitly. -# The action's auto-generated GitHub App installation token (claude[bot]) -# lacks `discussions: write` and silently fails every Discussion mutation. -# Passing GITHUB_TOKEN makes the job-level permissions grant apply. -# - `ANTHROPIC_MODEL: claude-opus-4-6` is set as a step env var. Opus is -# required for the depth of reasoning the multi-skill pipeline expects; -# Sonnet runs cheaper but produces shallower adversarial passes. -# - Do NOT enable `show_full_output: true`. It echoes raw tool results to -# public logs, which can leak secrets. +# 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 -# Reference implementation: https://github.com/petry-projects/TalkTerm/blob/main/.github/workflows/feature-ideation.yml - name: Feature Research & Ideation (BMAD Analyst) on: @@ -43,11 +23,11 @@ on: workflow_dispatch: inputs: focus_area: - description: 'Optional focus area (e.g., "accessibility", "performance", "developer experience")' + description: 'Optional focus area (e.g., "accessibility", "performance")' required: false type: string research_depth: - description: 'Research depth — controls web search volume and iteration count' + description: 'Research depth' required: false default: 'standard' type: choice @@ -63,602 +43,18 @@ concurrency: cancel-in-progress: false jobs: - # --------------------------------------------------------------------------- - # Job 1: Gather signals — issues, discussions, releases, community feedback - # --------------------------------------------------------------------------- - gather-signals: - name: Gather Project Signals - runs-on: ubuntu-latest - timeout-minutes: 10 - permissions: - contents: read - issues: read - pull-requests: read - discussions: read - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - outputs: - signals_artifact: ${{ steps.collect.outputs.signals_artifact }} - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 1 - - - name: Collect project signals - id: collect - run: | - set -euo pipefail - REPO="${{ github.repository }}" - SIGNALS_FILE=$(mktemp) - - # --- Open issues (exclude bot-generated, omit body to stay within output limits) --- - OPEN_ISSUES=$(gh issue list --repo "$REPO" --state open --limit 50 \ - --json number,title,labels,createdAt,author \ - -q '[.[] | select(.author.login != "dependabot[bot]" and .author.login != "github-actions[bot]")]' \ - 2>/dev/null || echo '[]') - - # --- Recently closed issues (last 30 days) --- - THIRTY_DAYS_AGO=$(date -u -d '30 days ago' +%Y-%m-%d 2>/dev/null || date -u -v-30d +%Y-%m-%d) - CLOSED_ISSUES=$(gh issue list --repo "$REPO" --state closed --limit 30 \ - --json number,title,labels,closedAt \ - -q "[.[] | select(.closedAt >= \"${THIRTY_DAYS_AGO}\")]" \ - 2>/dev/null || echo '[]') - - # --- Existing Ideas discussions (to detect what's already tracked) --- - # First resolve the Ideas category ID, then query filtered by category. - IDEAS_CAT_ID=$(gh api graphql -f query=' - query($repo: String!, $owner: String!) { - repository(name: $repo, owner: $owner) { - discussionCategories(first: 20) { - nodes { id name } - } - } - }' -f owner="${{ github.repository_owner }}" \ - -f repo="${{ github.event.repository.name }}" \ - --jq '[.data.repository.discussionCategories.nodes[] | select(.name == "Ideas")][0].id' \ - 2>/dev/null || echo "") - - if [ -n "$IDEAS_CAT_ID" ] && [ "$IDEAS_CAT_ID" != "null" ]; then - IDEAS_DISCUSSIONS=$(gh api graphql -f query=' - query($repo: String!, $owner: String!, $categoryId: ID!) { - repository(name: $repo, owner: $owner) { - discussions(first: 100, orderBy: {field: UPDATED_AT, direction: DESC}, - categoryId: $categoryId) { - nodes { - id number title - createdAt updatedAt - labels(first: 10) { nodes { name } } - comments(first: 1) { totalCount } - } - } - } - }' -f owner="${{ github.repository_owner }}" \ - -f repo="${{ github.event.repository.name }}" \ - -f categoryId="$IDEAS_CAT_ID" \ - --jq '.data.repository.discussions.nodes' \ - 2>/dev/null || echo '[]') - else - IDEAS_DISCUSSIONS='[]' - fi - - # --- Recent releases --- - RELEASES=$(gh release list --repo "$REPO" --limit 5 \ - --json tagName,name,publishedAt,isPrerelease \ - 2>/dev/null || echo '[]') - - # --- PR activity (merged last 30 days — what's been shipping) --- - MERGED_PRS=$(gh pr list --repo "$REPO" --state merged --limit 30 \ - --json number,title,labels,mergedAt \ - -q "[.[] | select(.mergedAt >= \"${THIRTY_DAYS_AGO}\")]" \ - 2>/dev/null || echo '[]') - - # --- Feature requests (issues labeled enhancement/feature) --- - FEATURE_REQUESTS=$(echo "$OPEN_ISSUES" | jq -c \ - '[.[] | select(.labels | map(.name) | any(test("enhancement|feature|idea"; "i")))]') - - # --- Bug patterns (open bugs — indicate pain points) --- - BUG_REPORTS=$(echo "$OPEN_ISSUES" | jq -c \ - '[.[] | select(.labels | map(.name) | any(test("bug"; "i")))]') - - # --- Compose signals --- - jq -n \ - --arg scan_date "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ - --arg repo "$REPO" \ - --argjson open_issues "$OPEN_ISSUES" \ - --argjson closed_issues "$CLOSED_ISSUES" \ - --argjson ideas_discussions "$IDEAS_DISCUSSIONS" \ - --argjson releases "$RELEASES" \ - --argjson merged_prs "$MERGED_PRS" \ - --argjson feature_requests "$FEATURE_REQUESTS" \ - --argjson bug_reports "$BUG_REPORTS" \ - '{ - scan_date: $scan_date, - repo: $repo, - open_issues: { count: ($open_issues | length), items: $open_issues }, - closed_issues_30d: { count: ($closed_issues | length), items: $closed_issues }, - ideas_discussions: { count: ($ideas_discussions | length), items: $ideas_discussions }, - releases: $releases, - merged_prs_30d: { count: ($merged_prs | length), items: $merged_prs }, - feature_requests: { count: ($feature_requests | length), items: $feature_requests }, - bug_reports: { count: ($bug_reports | length), items: $bug_reports } - }' > "$SIGNALS_FILE" - - cp "$SIGNALS_FILE" "${{ runner.temp }}/signals.json" - echo "signals_artifact=signals" >> "$GITHUB_OUTPUT" - - # Step summary - { - echo "## Signals Collected" - echo "" - echo "- **Open issues:** $(jq '.open_issues.count' "$SIGNALS_FILE")" - echo "- **Feature requests:** $(jq '.feature_requests.count' "$SIGNALS_FILE")" - echo "- **Bug reports:** $(jq '.bug_reports.count' "$SIGNALS_FILE")" - echo "- **Merged PRs (30d):** $(jq '.merged_prs_30d.count' "$SIGNALS_FILE")" - echo "- **Existing Ideas discussions:** $(jq '.ideas_discussions.count' "$SIGNALS_FILE")" - } >> "$GITHUB_STEP_SUMMARY" - - rm -f "$SIGNALS_FILE" - - - name: Upload signals artifact - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: signals - path: ${{ runner.temp }}/signals.json - retention-days: 7 - - # --------------------------------------------------------------------------- - # Job 2: BMAD Analyst — Research, ideate, and post per-idea Discussions - # --------------------------------------------------------------------------- - analyze: - name: BMAD Analyst — Feature Ideation - needs: [gather-signals] - runs-on: ubuntu-latest - timeout-minutes: 60 - permissions: - contents: read - discussions: write - id-token: write - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 1 - - - name: Download signals artifact - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - with: - name: signals - path: ${{ runner.temp }} - - - name: Run Claude Code — BMAD Analyst - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - FOCUS_AREA: ${{ inputs.focus_area || '' }} - RESEARCH_DEPTH: ${{ inputs.research_depth || 'standard' }} - # Opus 4.6 is required — see header comment for why. - ANTHROPIC_MODEL: claude-opus-4-6 - # === CUSTOMISE THIS PER REPO === - # Replace this paragraph with a 3-5 sentence description of your project: - # what it is, who it serves, and the competitive landscape Mary should - # research. The more specific you are, the more useful the proposals. - PROJECT_CONTEXT: | - 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." - uses: anthropics/claude-code-action@6e2bd52842c65e914eba5c8badd17560bd26b5de # v1.0.89 - with: - claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - # CRITICAL: pass the workflow's GITHUB_TOKEN explicitly so the - # job-level `permissions: discussions: write` grant applies. Without - # this, the action uses its own GitHub App token (claude[bot]) which - # lacks discussions:write and silently fails every mutation. - github_token: ${{ secrets.GITHUB_TOKEN }} - claude_args: "--allowedTools Bash,Read,Glob,Grep,WebSearch,WebFetch" - prompt: | - You are **Mary**, the BMAD Strategic Business Analyst, running as an automated - weekly feature research and ideation agent for the **${{ github.repository }}** project. - - ## Thinking & Research Directive - - **Think hard.** Take your time with every phase below. Reason deeply about each - finding before moving on. Do not rush to conclusions — sit with the evidence, - look for non-obvious patterns, and challenge your own assumptions. - - **Research freely — time is no constraint.** You have the full timeout budget to - produce the highest-quality analysis possible. Prefer doing one more web search - over guessing. Prefer reading one more source over assuming. Depth and rigor - matter far more than speed. - - ## Your Persona - - You are a senior analyst with deep expertise in market research, competitive analysis, - and requirements elicitation. You approach every analysis like a treasure hunt — - thrilled by every clue, energized when patterns emerge. You structure insights with - precision while making analysis feel like discovery. You draw upon Porter's Five Forces, - SWOT analysis, and competitive intelligence methodologies naturally. - - ## Project Context - - $PROJECT_CONTEXT - - ## Environment - - - Repository: ${{ github.repository }} - - Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - - Date: $(date -u +%Y-%m-%d) - - Focus area: $FOCUS_AREA (empty = open-ended exploration) - - Research depth: $RESEARCH_DEPTH - - ## Phase 1: Load Context - - 1. Read the project signals collected by the previous job: - ```bash - cat ${{ runner.temp }}/signals.json - ``` - - 2. Discover and read planning artifacts. BMAD installations vary — - use Glob to find them, then Read the ones that exist: - ``` - Glob: _bmad/**/*.md - Glob: _bmad-output/**/*.md - Glob: docs/planning/**/*.md - Glob: docs/architecture/**/*.md - ``` - Prioritise files named like `prd*.md`, `architecture*.md`, - `epics*.md`, `roadmap*.md`, `ux-*.md`, `requirements*.md`. - - 3. Read the project README and any recent changelog or release notes. - - 4. Scan the codebase for extensibility points and underutilized capabilities. - Use Glob and Grep to find: - - Interface/port files (e.g. `**/ports/*.ts`, `**/interfaces/*.go`, - `**/abstract*.py`) — these often hint at planned extensions. - - Adapter / strategy / factory patterns — extension seams. - - TODO / FIXME / HACK comments — known gaps the team has flagged. - - Files named `*config*`, `*settings*` — surfaces user-facing knobs. - - 5. **Read existing Ideas discussions from signals.** These are ideas already being - tracked. You will update them if you have new findings, or leave them alone. - - ## Phase 2: Market Research Skill (Iterative Evidence Gathering) - - **Adopt the "Market Research" mindset.** Your sole job in this phase is to gather - hard evidence — not to generate ideas yet. Be a detective, not a brainstormer. - - Using the **Project Context** above as your starting point, research the - competitive landscape and emerging trends. Cover at minimum: - - ### Competitive Landscape - - What are competitors in this space shipping recently? - - What UX patterns or capabilities are emerging as table stakes? - - Who is winning, who is losing, and why? - - What user segments are underserved? - - ### Emerging Capabilities - - New platform / SDK / model / framework features that unlock new approaches - - Industry trend reports, analyst commentary, conference announcements - - Technology shifts that change what is feasible or affordable - - ### User Need Signals - - Patterns in open issues and feature requests - - Common pain points from bug reports that suggest UX gaps - - What shipped recently (merged PRs) and what gaps remain - - Community discussion themes - - Adjust depth based on RESEARCH_DEPTH: - - **quick**: Skim signals, 1-2 web searches, focus on low-hanging fruit - - **standard**: Thorough signal analysis, 5-8 web searches, balanced exploration - - **deep**: Exhaustive research, 10+ web searches, competitor deep dives, trend analysis - - **Iterate:** After your first research pass, review what you found. Are there gaps? - Promising threads you didn't pull? Do another pass on the most promising areas - before moving on. Keep going until you feel you have a solid evidence base. - - ## Phase 3: Brainstorming Skill (Divergent Ideation) - - **Switch to the "Brainstorming" mindset.** Now generate as many ideas as possible — - quantity over quality. No idea is too wild at this stage. Build on the evidence - from Phase 2 but let your imagination run. - - Generate **8-15 raw feature ideas** across these lenses: - - What gaps do competitors leave that this project could fill? - - What emerging tech could unlock entirely new capabilities? - - What pain points from issues/bugs hint at deeper unmet needs? - - What would make this project indispensable for a specific user persona? - - What would a "10x better" version of an existing feature look like? - - For each raw idea, write a one-line description and note which evidence supports it. - - **Iterate:** Review your list. Can you combine two weaker ideas into one stronger - idea? Did generating ideas reveal a research gap? If so, go back to Phase 2 for - a targeted search, then return here. Repeat until the ideas feel grounded and - diverse. - - ## Phase 4: Party Mode Skill (Collaborative Refinement) - - **Switch to the "Party Mode" mindset.** Imagine you are a room of enthusiastic - product people, engineers, and designers who are all excited about these ideas. - The energy is high. Each person builds on the previous person's suggestion. - - Take the top 5-8 ideas from Phase 3 and refine them collaboratively: - - 1. **Amplify:** For each idea, ask "What if we went even further?" Push the idea - to its most ambitious form. - 2. **Connect:** Look for synergies — can two ideas combine into something greater - than the sum of their parts? - 3. **Ground:** For each amplified idea, ask "What is the minimum version that still - delivers the core value?" Find the sweet spot between ambition and feasibility. - 4. **Score:** For each refined idea, assess: - - | Dimension | Question | - |-----------|----------| - | **Feasibility** | Can we build this with the current stack? How much effort? | - | **Impact** | How many users does this serve? How much value does it add? | - | **Urgency** | Is the market moving here now? Is there competitive pressure? | - - Select the **top 5 ideas** scoring high on at least 2 of 3 dimensions. - - ## Phase 5: Adversarial Skill (Stress-Test & Defend) - - **Switch to the "Adversarial" mindset.** You are now a skeptical VP of Product, - a resource-constrained engineering lead, and a demanding end-user — all at once. - Your job is to tear these ideas apart. Only the ones that survive are worth proposing. - - For each of the top 5 ideas from Phase 4, apply these challenges: - - 1. **"So what?"** — Why would a user actually switch to or stay with this project - because of this? If you can't articulate a compelling user story, cut it. - 2. **"Who else?"** — Is someone else already doing this better, or about to? - If this project can't differentiate, cut it or find the unique angle. - 3. **"At what cost?"** — What is the true engineering cost (not the optimistic - estimate)? What would we have to stop doing to build this? If the opportunity - cost is too high, cut it. - 4. **"What breaks?"** — What existing functionality or architectural assumptions - does this violate? If it creates technical debt or UX inconsistency, can that - be mitigated? - 5. **"Prove it."** — What evidence from Phase 2 actually supports demand for this? - Gut feelings and "it would be cool" are not evidence. Cite specific signals. - - **After the adversarial pass, you should have 3-5 ROBUST and DEFENSIBLE proposals.** - Each surviving idea must have: - - At least one concrete market signal (competitor move, user request, or trend) - - A clear user story explaining why someone would care - - A feasible technical path grounded in the current architecture - - A rebuttal to the strongest objection against it - - If fewer than 3 ideas survive, that is fine — quality over quantity. - If a focus area was specified, weight proposals toward that area but don't - exclude other high-scoring discoveries. - - ## Phase 6: Resolve Discussion Category & Repository IDs - - **Auth note:** All `gh` and `gh api` commands in the phases below use - the `GH_TOKEN` environment variable (already set to the workflow's - `GITHUB_TOKEN`), which has `discussions: write` permission via the - job-level grant. You do NOT need to authenticate manually. - - Before creating or updating Discussions, you need the repository ID and the - "Ideas" discussion category ID. - - ```bash - gh api graphql -f query=' - query($repo: String!, $owner: String!) { - repository(name: $repo, owner: $owner) { - id - discussionCategories(first: 20) { - nodes { id name } - } - } - }' -f owner="${{ github.repository_owner }}" \ - -f repo="${{ github.event.repository.name }}" - ``` - - Save the repository ID and the "Ideas" category ID. If "Ideas" does not exist, - fall back to "General". If neither exists, skip Discussion creation and write - an error to the step summary. - - ## Phase 7: Create or Update Per-Idea Discussions - - For **each** feature proposal surviving Phase 5, check whether a matching Discussion - already exists in the Ideas category. - - ### Matching Logic - - Search existing Ideas discussions (from the signals data) for a title that - matches the proposal's core concept. Use fuzzy matching — the exact title - may differ but the idea should be recognizably the same. - - **Discussion title format:** `💡 ` - - ### If the Discussion DOES NOT exist → Create it - - ```bash - gh api graphql -f query=' - mutation($repoId: ID!, $categoryId: ID!, $title: String!, $body: String!) { - createDiscussion(input: { - repositoryId: $repoId, - categoryId: $categoryId, - title: $title, - body: $body - }) { - discussion { id number url } - } - }' -f repoId="" \ - -f categoryId="" \ - -f title="💡 " \ - -f body="" - ``` - - **New Discussion body format:** - - ```markdown - ## Summary - - <2-3 sentence description of the feature idea> - - ## Market Signal - - - - ## User Signal - - - - ## Technical Opportunity - - - - ## Assessment - - | Dimension | Score | Rationale | - |-----------|-------|-----------| - | Feasibility | high/med/low | ... | - | Impact | high/med/low | ... | - | Urgency | high/med/low | ... | - - ## Adversarial Review - - **Strongest objection:** - **Rebuttal:** - - ## Suggested Next Step - - - - --- - *Proposed by the [BMAD Analyst (Mary)](/${{ github.repository }}/actions/workflows/feature-ideation.yml) on .* - *[Workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})* - ``` - - After creating, add the `enhancement` label to the discussion if your repo - has one. Resolve the label ID with: - ```bash - gh api graphql -f query=' - query($repo: String!, $owner: String!) { - repository(name: $repo, owner: $owner) { - label(name: "enhancement") { id } - } - }' -f owner="${{ github.repository_owner }}" \ - -f repo="${{ github.event.repository.name }}" - ``` - Then apply via `addLabelsToLabelable`. Skip silently if the label doesn't exist. - - ### If the Discussion ALREADY exists → Add an update comment - - When a prior run already created a Discussion for the same idea, add a comment - with new developments rather than creating a duplicate. - - ```bash - gh api graphql -f query=' - mutation($discussionId: ID!, $body: String!) { - addDiscussionComment(input: { - discussionId: $discussionId, - body: $body - }) { - comment { id url } - } - }' -f discussionId="" \ - -f body="" - ``` - - **Update comment format:** - - ```markdown - ## Weekly Update — - - ### What Changed - - - - ### Updated Assessment - - | Dimension | Previous | Current | Delta | - |-----------|----------|---------|-------| - | Feasibility | ... | ... | ↑/↓/→ | - | Impact | ... | ... | ↑/↓/→ | - | Urgency | ... | ... | ↑/↓/→ | - - ### Recommendation - - - - --- - *Updated by the [BMAD Analyst (Mary)](/${{ github.repository }}/actions/workflows/feature-ideation.yml) on .* - *[Workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})* - ``` - - **Important:** Only add an update comment if there is genuinely new information. - If nothing has changed for an existing idea, skip it — don't post "no updates" comments. - - ### If an existing idea should be retired - - If research shows a previously proposed idea is no longer viable (market shifted, - already implemented, superseded by another approach), add a final comment - recommending closure: - - ```markdown - ## Retirement Recommendation — - - **Recommendation:** Close this proposal. - - **Reason:** - -
- - --- - *Reviewed by the [BMAD Analyst (Mary)](/${{ github.repository }}/actions/workflows/feature-ideation.yml) on .* - ``` - - Do NOT close the Discussion — only recommend closure for human decision. - - ## Phase 8: Step Summary - - Write to $GITHUB_STEP_SUMMARY: - - ```markdown - ## Feature Research & Ideation — - - **Focus:** - **Research depth:** - - ### New Ideas Proposed - | Discussion | Title | Feasibility | Impact | Urgency | - |------------|-------|-------------|--------|---------| - | #N | 💡 ... | ... | ... | ... | - - ### Existing Ideas Updated - | Discussion | Title | Key Change | - |------------|-------|------------| - | #N | 💡 ... | | - - ### Ideas Reviewed — No Update Needed - | Discussion | Title | Reason | - |------------|-------|--------| - | #N | 💡 ... | | - - ### Market & Trend Summary - <2-3 bullet points on the most notable trends this week> - - --- - *[Workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})* - ``` - - ## Rules - - - **Do NOT create issues or PRs.** Output goes to Discussion threads only. - - **Do NOT implement code.** This is research and ideation only. - - **Max 5 new Discussions per run.** Fewer is fine — quality over quantity. - - **Only propose ideas scoring high on 2+ dimensions** (feasibility/impact/urgency). - - **One Discussion per idea.** Never create duplicates — always check existing first. - - **Only comment on existing Discussions when there is new information.** No empty updates. - - **Ground proposals in evidence.** Every proposal must cite at least one market signal, - user signal, or technical opportunity — not just abstract ideas. - - **Be specific about next steps.** Each proposal should suggest a concrete action - (product brief, spike, discussion, etc.) not just "we should consider this." - - **Respect the project's architecture.** Proposals should be compatible with the - patterns documented in AGENTS.md and the planning artifacts. - - **Use the 💡 prefix** in all idea Discussion titles for easy identification. + ideate: + uses: petry-projects/.github/.github/workflows/feature-ideation-reusable.yml@main + 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: | + 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: + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} From 9b9a387235767daae0824cdaae0d298a1417f0b3 Mon Sep 17 00:00:00 2001 From: DJ Date: Mon, 6 Apr 2026 19:12:36 -0700 Subject: [PATCH 3/8] fix(lint): add shellcheck disable for GraphQL variable false positive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gh api graphql queries use $repo / $owner / $categoryId as GraphQL variables (not shell expansions), which must remain in single quotes. shellcheck SC2016 fires anyway — disable it for this script. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/feature-ideation-reusable.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/feature-ideation-reusable.yml b/.github/workflows/feature-ideation-reusable.yml index 9f4f7f3ec..bfa59ddd2 100644 --- a/.github/workflows/feature-ideation-reusable.yml +++ b/.github/workflows/feature-ideation-reusable.yml @@ -74,6 +74,9 @@ jobs: - name: Collect project signals run: | set -euo pipefail + # shellcheck disable=SC2016 + # The GraphQL queries below use `$variable` syntax for GraphQL + # variables, not shell expansion — single quotes are required. REPO="${{ github.repository }}" SIGNALS_FILE=$(mktemp) From 741d137191b1933f86af90ad20fb4087a053a143 Mon Sep 17 00:00:00 2001 From: DJ Date: Mon, 6 Apr 2026 19:14:40 -0700 Subject: [PATCH 4/8] fix(lint): use quoted heredocs for GraphQL queries to satisfy SC2016 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit actionlint runs shellcheck on the entire run script as one unit and ignores inline disable directives. Rewriting the gh api graphql calls to use cat <<'GRAPHQL' heredocs makes the GraphQL variable references ($repo, $owner, $categoryId) shell-inert without depending on single-quoted string literals — eliminating the SC2016 false positive. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../workflows/feature-ideation-reusable.yml | 60 +++++++++++-------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/.github/workflows/feature-ideation-reusable.yml b/.github/workflows/feature-ideation-reusable.yml index bfa59ddd2..5faad2b63 100644 --- a/.github/workflows/feature-ideation-reusable.yml +++ b/.github/workflows/feature-ideation-reusable.yml @@ -74,9 +74,6 @@ jobs: - name: Collect project signals run: | set -euo pipefail - # shellcheck disable=SC2016 - # The GraphQL queries below use `$variable` syntax for GraphQL - # variables, not shell expansion — single quotes are required. REPO="${{ github.repository }}" SIGNALS_FILE=$(mktemp) @@ -94,35 +91,46 @@ jobs: 2>/dev/null || echo '[]') # --- Existing Ideas discussions (to detect what's already tracked) --- - IDEAS_CAT_ID=$(gh api graphql -f query=' - query($repo: String!, $owner: String!) { - repository(name: $repo, owner: $owner) { - discussionCategories(first: 20) { - nodes { id name } - } + # NOTE: GraphQL queries use heredocs with quoted terminators so the + # `$repo` / `$owner` / `$categoryId` references stay literal (they + # are GraphQL variables, not shell expansions). + IDEAS_CAT_QUERY=$(cat <<'GRAPHQL' + query($repo: String!, $owner: String!) { + repository(name: $repo, owner: $owner) { + discussionCategories(first: 20) { + nodes { id name } } - }' -f owner="${{ github.repository_owner }}" \ - -f repo="${{ github.event.repository.name }}" \ + } + } + GRAPHQL + ) + IDEAS_CAT_ID=$(gh api graphql -f query="$IDEAS_CAT_QUERY" \ + -f owner="${{ github.repository_owner }}" \ + -f repo="${{ github.event.repository.name }}" \ --jq '[.data.repository.discussionCategories.nodes[] | select(.name == "Ideas")][0].id' \ 2>/dev/null || echo "") if [ -n "$IDEAS_CAT_ID" ] && [ "$IDEAS_CAT_ID" != "null" ]; then - IDEAS_DISCUSSIONS=$(gh api graphql -f query=' - query($repo: String!, $owner: String!, $categoryId: ID!) { - repository(name: $repo, owner: $owner) { - discussions(first: 100, orderBy: {field: UPDATED_AT, direction: DESC}, - categoryId: $categoryId) { - nodes { - id number title - createdAt updatedAt - labels(first: 10) { nodes { name } } - comments(first: 1) { totalCount } - } - } + IDEAS_DISC_QUERY=$(cat <<'GRAPHQL' + query($repo: String!, $owner: String!, $categoryId: ID!) { + repository(name: $repo, owner: $owner) { + discussions(first: 100, orderBy: {field: UPDATED_AT, direction: DESC}, + categoryId: $categoryId) { + nodes { + id number title + createdAt updatedAt + labels(first: 10) { nodes { name } } + comments(first: 1) { totalCount } } - }' -f owner="${{ github.repository_owner }}" \ - -f repo="${{ github.event.repository.name }}" \ - -f categoryId="$IDEAS_CAT_ID" \ + } + } + } + GRAPHQL + ) + IDEAS_DISCUSSIONS=$(gh api graphql -f query="$IDEAS_DISC_QUERY" \ + -f owner="${{ github.repository_owner }}" \ + -f repo="${{ github.event.repository.name }}" \ + -f categoryId="$IDEAS_CAT_ID" \ --jq '.data.repository.discussions.nodes' \ 2>/dev/null || echo '[]') else From 197b4fcd46ab40bf8b6caf29585ed3495a8efbf1 Mon Sep 17 00:00:00 2001 From: DJ Date: Mon, 6 Apr 2026 19:20:00 -0700 Subject: [PATCH 5/8] fix: expand prompt variables via Actions expressions, add placeholder guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit caught a critical latent bug inherited from the original TalkTerm prompt: shell-style $VAR and $(date) syntax inside the action's `prompt:` input is NOT expanded — the action receives literal text. This silently broke variable substitution in every prior run, but mattered most for the new reusable workflow because PROJECT_CONTEXT is now load-bearing. Changes: - Replace $PROJECT_CONTEXT, $FOCUS_AREA, $RESEARCH_DEPTH, and $(date ...) with ${{ inputs.* }} and ${{ github.run_started_at }} expressions, which ARE evaluated by GitHub before passing the prompt to the action. - Add a "Validate project_context is customised" pre-step that fails fast if an adopter copied the caller stub without replacing the TODO placeholder. Prevents wasted Opus runs producing generic Discussions. - scripts/compliance-audit.sh: detect BMAD repos via `_bmad-output/` as well as `_bmad/`, matching the broader detection rule documented in ci-standards.md §8 (TalkTerm only has `_bmad-output/`). Co-Authored-By: Claude Opus 4.6 (1M context) --- .../workflows/feature-ideation-reusable.yml | 33 +++++++++++++++---- scripts/compliance-audit.sh | 5 ++- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/.github/workflows/feature-ideation-reusable.yml b/.github/workflows/feature-ideation-reusable.yml index 5faad2b63..85942646c 100644 --- a/.github/workflows/feature-ideation-reusable.yml +++ b/.github/workflows/feature-ideation-reusable.yml @@ -221,12 +221,31 @@ jobs: name: signals path: ${{ runner.temp }} + - name: Validate project_context is customised + # Fail fast if an adopter copied the caller stub without replacing + # the placeholder. Without this, runs would burn cost producing + # generic Discussions about a "TODO: Replace this" project. + env: + PROJECT_CONTEXT: ${{ inputs.project_context }} + run: | + set -euo pipefail + if printf '%s' "$PROJECT_CONTEXT" | grep -q 'TODO: Replace this'; then + { + echo "## ❌ project_context not customised" + echo "" + echo "The reusable feature-ideation workflow was called with the" + echo "stock placeholder \`project_context\`. Edit your repo's" + echo "\`.github/workflows/feature-ideation.yml\` and replace the" + echo "TODO block with a 3-5 sentence description of the project," + echo "its target users, and the competitive landscape Mary should" + echo "research." + } >> "$GITHUB_STEP_SUMMARY" + exit 1 + fi + - name: Run Claude Code — BMAD Analyst env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - FOCUS_AREA: ${{ inputs.focus_area }} - RESEARCH_DEPTH: ${{ inputs.research_depth }} - PROJECT_CONTEXT: ${{ inputs.project_context }} # CRITICAL: Opus 4.6 is required for the depth the multi-skill # pipeline expects. The action does not expose model selection as # an input — it reads ANTHROPIC_MODEL from the environment. @@ -265,15 +284,15 @@ jobs: ## Project Context - $PROJECT_CONTEXT + ${{ inputs.project_context }} ## Environment - Repository: ${{ github.repository }} - Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - - Date: $(date -u +%Y-%m-%d) - - Focus area: $FOCUS_AREA (empty = open-ended exploration) - - Research depth: $RESEARCH_DEPTH + - Date: ${{ github.run_started_at }} + - Focus area: ${{ inputs.focus_area || 'Open exploration' }} + - Research depth: ${{ inputs.research_depth }} ## Phase 1: Load Context diff --git a/scripts/compliance-audit.sh b/scripts/compliance-audit.sh index 7302e1de3..d8883c56a 100755 --- a/scripts/compliance-audit.sh +++ b/scripts/compliance-audit.sh @@ -126,7 +126,10 @@ detect_ecosystems() { if echo "$tree" | grep -qE '\.github/workflows/.*\.yml$'; then ECOSYSTEMS+=("github-actions") fi - if echo "$tree" | grep -qE '(^|/)_bmad/'; then + # BMAD Method: detected via either the active install dir (`_bmad/`) or + # the planning artifacts output dir (`_bmad-output/`). Repos may have one, + # the other, or both depending on the BMAD workflow stage. + if echo "$tree" | grep -qE '(^|/)_bmad(-output)?/'; then ECOSYSTEMS+=("bmad-method") fi } From a3eacfdc301c9da1437e84ab1963a0bdcc8d93c7 Mon Sep 17 00:00:00 2001 From: DJ Date: Mon, 6 Apr 2026 19:22:17 -0700 Subject: [PATCH 6/8] fix(lint): drop github.run_started_at (not in actionlint context schema) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The agent can read scan_date from signals.json instead — added a hint in the Environment section. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/feature-ideation-reusable.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/feature-ideation-reusable.yml b/.github/workflows/feature-ideation-reusable.yml index 85942646c..c3e12a9ca 100644 --- a/.github/workflows/feature-ideation-reusable.yml +++ b/.github/workflows/feature-ideation-reusable.yml @@ -290,10 +290,12 @@ jobs: - Repository: ${{ github.repository }} - Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - - Date: ${{ github.run_started_at }} - Focus area: ${{ inputs.focus_area || 'Open exploration' }} - Research depth: ${{ inputs.research_depth }} + (The current date is in `signals.json` as `scan_date` — read it + from there when you need it for Discussion timestamps.) + ## Phase 1: Load Context 1. Read the project signals collected by the previous job: From f66bb5b9ea2dab77438ac9010b5afe15825cdfe3 Mon Sep 17 00:00:00 2001 From: DJ Date: Mon, 6 Apr 2026 19:27:47 -0700 Subject: [PATCH 7/8] fix(caller): grant cascading permissions on the calling job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit caught: the caller stub had `permissions: {}` at workflow level and no permissions block on the calling job. Reusable workflows inherit permissions from the calling job — without an explicit grant, the reusable workflow's `discussions: write` declaration would have nothing to apply, and Discussion mutations would fail with FORBIDDEN just like the original bug we fixed in TalkTerm. The reusable workflow's job-level permissions are documentation of what it needs; the caller is what actually grants them. Co-Authored-By: Claude Opus 4.6 (1M context) --- standards/workflows/feature-ideation.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/standards/workflows/feature-ideation.yml b/standards/workflows/feature-ideation.yml index 471ac848c..fa7b5ba94 100644 --- a/standards/workflows/feature-ideation.yml +++ b/standards/workflows/feature-ideation.yml @@ -44,6 +44,19 @@ 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 with: # === CUSTOMISE THIS PER REPO — the only required edit === From 1bd52decb5fcb6c3ed9560dc5d4f4041d7891b57 Mon Sep 17 00:00:00 2001 From: DJ Date: Mon, 6 Apr 2026 19:32:41 -0700 Subject: [PATCH 8/8] fix: use claude_args --model interface; instruct re-query before create Two more fixes from CodeRabbit review: 1. Model selection via claude_args (the documented v1 interface) instead of ANTHROPIC_MODEL env var. claude_args takes precedence over the env var per the action's docs, so depending on the env var was relying on undocumented behavior. The pinned v1.0.89 happens to honor ANTHROPIC_MODEL too (verified in TalkTerm run #3 logs), but the documented path is more robust against future action upgrades. 2. Re-query existing Ideas discussions before each create. The signals snapshot only fetches the first page of discussions (GraphQL caps connections at 100 per page) and only covers the Ideas category, not the General fallback. Mary now does a fresh query before each create to avoid duplicates in repos with >100 idea threads or where Ideas doesn't exist. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../workflows/feature-ideation-reusable.yml | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/feature-ideation-reusable.yml b/.github/workflows/feature-ideation-reusable.yml index c3e12a9ca..9c70cbec6 100644 --- a/.github/workflows/feature-ideation-reusable.yml +++ b/.github/workflows/feature-ideation-reusable.yml @@ -246,10 +246,6 @@ jobs: - name: Run Claude Code — BMAD Analyst env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # CRITICAL: Opus 4.6 is required for the depth the multi-skill - # pipeline expects. The action does not expose model selection as - # an input — it reads ANTHROPIC_MODEL from the environment. - ANTHROPIC_MODEL: ${{ inputs.model }} uses: anthropics/claude-code-action@6e2bd52842c65e914eba5c8badd17560bd26b5de # v1.0.89 with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} @@ -258,7 +254,11 @@ jobs: # this, the action uses its own GitHub App token (claude[bot]) which # lacks discussions:write and silently fails every mutation. github_token: ${{ secrets.GITHUB_TOKEN }} - claude_args: "--allowedTools Bash,Read,Glob,Grep,WebSearch,WebFetch" + # Model selection goes through claude_args per the v1 action's + # documented interface. Opus 4.6 is the default — see inputs.model. + claude_args: | + --model ${{ inputs.model }} + --allowedTools Bash,Read,Glob,Grep,WebSearch,WebFetch prompt: | You are **Mary**, the BMAD Strategic Business Analyst, running as an automated weekly feature research and ideation agent for the **${{ github.repository }}** project. @@ -467,11 +467,18 @@ jobs: ## Phase 7: Create or Update Per-Idea Discussions For **each** feature proposal surviving Phase 5, check whether a matching Discussion - already exists in the Ideas category. + already exists in the target category (Ideas, or General as fallback). ### Matching Logic - Search existing Ideas discussions (from the signals data) for a title that + **Re-query before each create.** The signals snapshot only contains the first + 100 Ideas discussions and may miss the General fallback category. Before + creating a new Discussion for a proposal, run a fresh GraphQL query for + existing discussions in the target category, filtered to titles starting with + `💡`. This avoids duplicates if the snapshot was incomplete or if a previous + run in this same execution already created a related Discussion. + + Search the fresh results (and the signals snapshot) for a title that matches the proposal's core concept. Use fuzzy matching — the exact title may differ but the idea should be recognizably the same.