Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
76d1f75
feat(feature-ideation): per-repo source list + feed checkpoint via la…
Apr 16, 2026
56fd3ca
fix(feature-ideation): validate ISO-8601 format for last_successful_r…
Apr 16, 2026
0425a17
fix(collect-signals): align bats stub order with new gh run list call
Apr 16, 2026
15045be
fix(compose-signals.bats): update call sites to 12-arg signature
Apr 17, 2026
a07e932
fix(review): address CodeRabbit and Copilot review comments
Apr 17, 2026
5641169
Merge branch 'main' into feat/feature-ideation-sources-list
don-petry Apr 17, 2026
81ca148
test: add self-test feature-ideation stub for dry-run validation
don-petry Apr 17, 2026
e7e04b8
fix: trailing newline + clean up stub
don-petry Apr 17, 2026
bc8e529
Merge branch 'main' into feat/feature-ideation-sources-list
don-petry Apr 17, 2026
f3dfa6b
fix: pin reusable workflow ref to commit SHA (SonarCloud)
don-petry Apr 17, 2026
11e2995
chore: remove temporary test stub (not for main)
don-petry Apr 17, 2026
689a2d7
fix(reusable): guard against empty sources_file in Phase 2 prompt
Apr 17, 2026
82649c5
Merge branch 'main' into feat/feature-ideation-sources-list
don-petry Apr 17, 2026
da7b2c9
fix(lint): move sources_file expression to env var to respect line-le…
Apr 17, 2026
d367993
fix(lint): resolve YAML syntax error in sources_file prompt guard
Apr 17, 2026
dff89b1
Merge branch 'main' into feat/feature-ideation-sources-list
don-petry Apr 17, 2026
8ffa692
feat(dotgithub): add feature-ideation caller stub for .github self-test
Apr 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/schemas/signals.schema.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/petry-projects/.github/blob/main/.github/schemas/signals.schema.json",
"$comment": "version: 1.0.0 — must match SCHEMA_VERSION in collect-signals.sh; enforced by bats",
"$comment": "version: 1.1.0 — must match SCHEMA_VERSION in collect-signals.sh; enforced by bats",
"title": "Feature Ideation Signals",
"description": "Canonical contract between collect-signals.sh and the BMAD Analyst (Mary) prompt. Any change to this schema is a breaking change to the workflow.",
"type": "object",
"required": [
"schema_version",
"scan_date",
"last_successful_run",
"repo",
"open_issues",
"closed_issues_30d",
Expand All @@ -28,6 +29,11 @@
"type": "string",
"format": "date-time"
},
"last_successful_run": {
"description": "ISO-8601 timestamp of the previous successful workflow run; used as a feed checkpoint by the analyst to skip already-reviewed content.",
"type": "string",
"format": "date-time"
},
"repo": {
"type": "string",
"pattern": "^[^/]+/[^/]+$"
Expand Down
41 changes: 40 additions & 1 deletion .github/scripts/feature-ideation/collect-signals.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ set -euo pipefail
# if the constants drift, AND the bats `signals-schema: SCHEMA_VERSION
# constant matches schema file` test enforces this in CI.
# Caught by CodeRabbit review on PR petry-projects/.github#85.
SCHEMA_VERSION="1.0.0"
SCHEMA_VERSION="1.1.0"

SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib/gh-safe.sh
Expand Down Expand Up @@ -71,6 +71,43 @@ main() {
local scan_date
scan_date=$(date_now_iso)

# --- Feed checkpoint: last successful run ----------------------------------
# Used by the analyst to skip feed entries already reviewed. The current run
# is still in-progress, so --status=success --limit=1 reliably returns the
# previous successful run. Falls back to 30 days ago on first-ever run or
# after a long gap so the initial scan is bounded.
# WORKFLOW_FILE — caller-supplied env var for repos that name their stub
# something other than the conventional "feature-ideation.yml". Defaults to
# the conventional name; no change needed for repos that follow the standard.
printf '[collect-signals] resolving feed checkpoint (last successful run)\n' >&2
local last_successful_run _run_stderr _run_err
_run_stderr=$(mktemp)
last_successful_run=$(gh run list \
--repo "$REPO" \
--workflow="${WORKFLOW_FILE:-feature-ideation.yml}" \
--status=success \
--limit=1 \
--json createdAt \
--jq '.[0].createdAt // empty' \
2>"$_run_stderr" || true)
_run_err=$(cat "$_run_stderr")
rm -f "$_run_stderr"
# Validate that the result looks like an ISO-8601 datetime. The real `gh`
# CLI applies the --jq filter and emits a bare timestamp; in test environments
# the gh stub returns raw fixture JSON (without applying --jq), so we guard
# against that here rather than requiring every test to stub this extra call.
if [ -z "$last_successful_run" ] || [ "$last_successful_run" = "null" ] || \
! printf '%s' "$last_successful_run" | grep -qE '^[0-9]{4}-[0-9]{2}-[0-9]{2}T'; then
if [ -n "$_run_err" ]; then
printf '[collect-signals] gh run list warning: %s\n' "$_run_err" >&2
fi
last_successful_run="$(date_days_ago 30)T00:00:00Z"
printf '[collect-signals] no prior successful run found; using 30-day fallback: %s\n' \
"$last_successful_run" >&2
else
printf '[collect-signals] feed checkpoint: %s\n' "$last_successful_run" >&2
fi
Comment thread
don-petry marked this conversation as resolved.

local truncation_warnings='[]'

# --- Open issues -----------------------------------------------------------
Expand Down Expand Up @@ -201,6 +238,7 @@ GRAPHQL
"$bug_reports" \
"$REPO" \
"$scan_date" \
"$last_successful_run" \
"$SCHEMA_VERSION" \
"$truncation_warnings")

Expand All @@ -217,6 +255,7 @@ GRAPHQL
printf -- '- **Bug reports:** %s\n' "$(jq '.bug_reports.count' "$output_path")"
printf -- '- **Merged PRs (30d):** %s\n' "$(jq '.merged_prs_30d.count' "$output_path")"
printf -- '- **Existing Ideas discussions:** %s\n' "$(jq '.ideas_discussions.count' "$output_path")"
printf -- '- **Feed checkpoint (last successful run):** %s\n' "$(jq -r '.last_successful_run' "$output_path")"
local warn_count
warn_count=$(jq '.truncation_warnings | length' "$output_path")
if [ "$warn_count" -gt 0 ]; then
Expand Down
16 changes: 10 additions & 6 deletions .github/scripts/feature-ideation/lib/compose-signals.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,17 @@
# $7 bug_reports
# $8 repo (string, e.g. "petry-projects/talkterm")
# $9 scan_date (ISO-8601 string)
# $10 schema_version (string)
# $11 truncation_warnings (JSON array, may be [])
# $10 last_successful_run (ISO-8601 string; feed checkpoint)
# $11 schema_version (string)
# $12 truncation_warnings (JSON array, may be [])
#
# Output: signals.json document on stdout.

set -euo pipefail

compose_signals() {
if [ "$#" -ne 11 ]; then
printf '[compose-signals] expected 11 args, got %d\n' "$#" >&2
if [ "$#" -ne 12 ]; then
printf '[compose-signals] expected 12 args, got %d\n' "$#" >&2
return 64 # EX_USAGE
fi

Expand All @@ -40,8 +41,9 @@ compose_signals() {
local bug_reports="$7"
local repo="$8"
local scan_date="$9"
local schema_version="${10}"
local truncation_warnings="${11}"
local last_successful_run="${10}"
local schema_version="${11}"
local truncation_warnings="${12}"

# Validate every JSON input before composition. Better to fail loudly here
# than to let `jq --argjson` produce a cryptic parse error.
Expand All @@ -57,6 +59,7 @@ compose_signals() {

jq -n \
--arg scan_date "$scan_date" \
--arg last_successful_run "$last_successful_run" \
--arg repo "$repo" \
--arg schema_version "$schema_version" \
--argjson open_issues "$open_issues" \
Expand All @@ -70,6 +73,7 @@ compose_signals() {
'{
schema_version: $schema_version,
scan_date: $scan_date,
last_successful_run: $last_successful_run,
repo: $repo,
open_issues: { count: ($open_issues | length), items: $open_issues },
closed_issues_30d: { count: ($closed_issues | length), items: $closed_issues },
Expand Down
70 changes: 67 additions & 3 deletions .github/workflows/feature-ideation-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
# 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.
# place propagates to every BMAD-enabled repo after the v1 tag is bumped
# and their next scheduled run executes. Repos tracking @main pick it up
# immediately on their next run.
# - The two critical gotchas (github_token override, ANTHROPIC_MODEL env var)
# are baked in here so callers cannot accidentally regress them.
#
Expand Down Expand Up @@ -76,6 +78,16 @@ on:
required: false
default: 'v1'
type: string
sources_file:
description: |
Path (relative to repo root) of the repo-local reputable source list.
Defaults to .github/feature-ideation-sources.md. Copy
standards/feature-ideation-sources.md from petry-projects/.github as
a starting point and customise it for your project. If the file is
absent Mary falls back to open web search automatically.
required: false
default: '.github/feature-ideation-sources.md'
type: string
secrets:
CLAUDE_CODE_OAUTH_TOKEN:
description: 'Claude Code OAuth token (org-level secret)'
Expand All @@ -94,6 +106,7 @@ jobs:
issues: read
pull-requests: read
discussions: read
actions: read
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
Expand Down Expand Up @@ -199,6 +212,7 @@ jobs:
MATCH_PLAN_PATH: ${{ runner.temp }}/match-plan.json
TOOLING_DIR: ${{ github.workspace }}/.feature-ideation-tooling/.github/scripts/feature-ideation
FOCUS_AREA: ${{ inputs.focus_area || '' }}
SOURCES_FILE_PATH: ${{ inputs.sources_file }}
RESEARCH_DEPTH: ${{ inputs.research_depth }}
uses: anthropics/claude-code-action@905d4eb99ab3d43143d74fb0dcae537f29ac330a # v1.0.97
with:
Expand Down Expand Up @@ -295,8 +309,58 @@ jobs:
**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:
### Start from the repo-local Reputable Source List

Before running open-ended web searches, **check for a repo-local source list**.
If `$SOURCES_FILE_PATH` is non-empty, read it:

```
Read: $SOURCES_FILE_PATH
```

If the path is empty or the file does not exist, log a one-line warning
in the step summary under "Source list status" and proceed with open web search.

That file (if present) lists vendor blogs, RSS feeds, podcasts, and YouTube
channels this repo's maintainers trust as primary signal sources, organised
by category. Treat it as your **starting set**:

- Pick the categories most relevant to the **Project Context** above. A
CLI dev tool warrants different sources than a security scanner or a
data pipeline.
- For each relevant source, do a targeted `WebFetch` or `WebSearch`
scoped to that domain to find what's new. Prefer fetching the listed
RSS/Atom feed URL when one is given — it gives you structured "what
changed" data without scraping HTML.
- The list is a **floor, not a ceiling.** If a thread takes you to a
source not in the list and that source is reputable, follow it. After
the run, if a high-value source kept coming up but wasn't listed,
**mention it in the step summary** under a "Suggested additions to
source list" heading so a human can PR it in.
- If the file is missing or empty, log a warning to the step summary
and proceed with open web search — do not fail the run.
Comment thread
don-petry marked this conversation as resolved.

### Use the feed checkpoint to avoid re-reviewing old content

`$SIGNALS_PATH` contains a `last_successful_run` ISO-8601 timestamp — the
date/time the previous successful run of this workflow completed.

**When fetching any RSS/Atom feed or scraping a changelog/blog:**
- Only consider entries whose `pubDate` / `updated` / `published` field
is **after** `last_successful_run`.
- If a feed does not expose dates, or if all entries predate the checkpoint,
note that in your research log and move on — do not re-summarise content
you have no evidence is new.
- If `last_successful_run` is more than 60 days in the past (e.g. after a
long workflow outage), ignore the checkpoint and process the full feed to
avoid missing major developments.

This keeps each weekly run focused on genuinely new signal rather than
re-processing the same content at Opus 4.6 cost.

Using the **Project Context** above as your starting point and the
source list as your seed, research the competitive landscape and
emerging trends. Cover at minimum:

### Competitive Landscape
- What are competitors in this space shipping recently?
Expand Down
88 changes: 88 additions & 0 deletions .github/workflows/feature-ideation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# ─────────────────────────────────────────────────────────────────────────────
# SOURCE OF TRUTH: petry-projects/.github/standards/workflows/feature-ideation.yml
# Standard: petry-projects/.github/standards/ci-standards.md#8
# Reusable: petry-projects/.github/.github/workflows/feature-ideation-reusable.yml
#
# AGENTS — READ BEFORE EDITING:
# • This file is a THIN CALLER STUB. The 5-phase ideation pipeline, the
# Opus 4.6 model selection, the github_token override, and the
# ANTHROPIC_MODEL env var all live in the reusable workflow above.
# • You MAY change: the `project_context` value and optionally the cron
# schedule.
# • You MUST NOT change: trigger event shape, the `uses:` line, the
# job-level `permissions:` block, or the `secrets:` block.
# • If you need different behaviour, open a PR against the reusable in
# this repo. The change will propagate after the v1 tag is bumped.
# ─────────────────────────────────────────────────────────────────────────────
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., "CI patterns", "agent security")'
required: false
type: string
research_depth:
description: 'Research depth'
required: false
default: 'standard'
type: choice
options:
- quick
- standard
- deep
dry_run:
description: 'Log Discussion mutations to artifact instead of writing live'
required: false
default: false
type: boolean

permissions: {}

concurrency:
group: feature-ideation
cancel-in-progress: false

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)
# - actions: read (feed checkpoint via gh run list)
# - id-token: write (claude-code-action OIDC for GitHub App token)
permissions:
contents: read
issues: read
pull-requests: read
discussions: write
actions: read
id-token: write
uses: petry-projects/.github/.github/workflows/feature-ideation-reusable.yml@208ec2d69b75227d375edf8745d84fbac05a76b2 # v1 — bump SHA after tag update
with:
project_context: |
petry-projects/.github is the org-level standards and tooling repository
for the petry-projects GitHub organisation. It owns the canonical CI/CD
standards (ci-standards.md), all reusable GitHub Actions workflows
(claude-code, feature-ideation, agent-shield, dependabot), the BMAD
Method agentic development framework, and org-wide security and
compliance tooling. Downstream consumers are internal product repos
(Broodly, TalkTerm, markets). Key trends to track: GitHub Actions
platform improvements (larger runners, step summaries, OIDC advances),
agentic CI patterns (LLM-driven PR review, automated ideation,
agent-shield threat models), AI-assisted code review tooling evolution
(CodeRabbit, Copilot Workspace), security hardening for AI-agent
workflows (prompt injection, supply-chain pinning, least-privilege
scopes), and developer-experience standards for LLM-heavy codebases.
No public users — this is internal DevX infrastructure.
sources_file: 'standards/feature-ideation-sources.md'
focus_area: ${{ inputs.focus_area || '' }}
research_depth: ${{ inputs.research_depth || 'standard' }}
dry_run: ${{ inputs.dry_run || false }}
secrets:
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
Loading
Loading