Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
65 changes: 62 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 @@ -295,8 +308,54 @@ 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**:

```
Read: ${{ inputs.sources_file }}
```

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
27 changes: 22 additions & 5 deletions standards/ci-standards.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ where to send a fix when behavior needs to change.

| Tier | Examples | What lives in `standards/workflows/` | Where logic lives | Edits allowed in adopting repo |
|---|---|---|---|---|
| **1. Stub** | `claude.yml`, `dependency-audit.yml`, `dependabot-automerge.yml`, `dependabot-rebase.yml`, `agent-shield.yml`, `feature-ideation.yml` | A thin caller stub that delegates via `uses: petry-projects/.github/.github/workflows/<name>-reusable.yml@v1` | The matching `*-reusable.yml` in this repo (single source of truth) | **None** in normal use. May tune `with:` inputs where the reusable exposes them (e.g. `agent-shield` accepts `min-severity`, `required-files`; `feature-ideation` requires `project_context`). To change behavior, open a PR against the reusable in this repo — the change propagates everywhere on next run. |
| **1. Stub** | `claude.yml`, `dependency-audit.yml`, `dependabot-automerge.yml`, `dependabot-rebase.yml`, `agent-shield.yml`, `feature-ideation.yml` | A thin caller stub that delegates via `uses: petry-projects/.github/.github/workflows/<name>-reusable.yml@v1` | The matching `*-reusable.yml` in this repo (single source of truth) | **None** in normal use. May tune `with:` inputs where the reusable exposes them (e.g. `agent-shield` accepts `min-severity`, `required-files`; `feature-ideation` requires `project_context`). To change behavior, open a PR against the reusable in this repo — repos on `@v1` pick it up after the `v1` tag is bumped; repos on `@main` pick it up on their next run. |
| **2. Per-repo template** | `ci.yml`, `sonarcloud.yml` | _(no template — see the patterns documented below)_ | In each repo, because the workflow is tech-stack-specific (language matrix, build tool, test framework) | **Limited.** Each adopting repo carries its own copy. Stay within the patterns in this document; do not change action SHAs, permission scopes, trigger events, or job names without raising a standards PR first. |
| **GitHub-managed** | CodeQL default setup | _(no workflow file — managed via repo Settings → Code security)_ | GitHub | None. Configured via `apply-repo-settings.sh`; per-repo `codeql.yml` files are treated as drift by the compliance audit. See [§2 CodeQL Analysis](#2-codeql-analysis-github-managed-default-setup). |
| **3. Free per-repo** | `release.yml`, project-specific automation | _(out of scope for this standard)_ | Per-repo | Free, but must still comply with the [Action Pinning Policy](#action-pinning-policy) and the [Required Workflows](#required-workflows) constraints. |
Expand Down Expand Up @@ -475,8 +475,21 @@ split into two parts:
Defines the schedule, the `workflow_dispatch` inputs, and calls the
reusable workflow with a single required parameter: `project_context`.

3. **Reputable Source List** (repo-local, per-repo):
Each adopting repo maintains its own copy at `.github/feature-ideation-sources.md`
(or the path passed via the `sources_file` workflow input).
Use [`standards/feature-ideation-sources.md`](feature-ideation-sources.md)
as a starter template, then customise it for your project. The Phase 2 prompt
instructs Mary to read that file as her **starting set** for market research —
vendor blogs, RSS feeds, podcasts, and YouTube channels organised by category.
If the file is absent Mary falls back to open web search automatically.
Each repo owns its own copy; add or remove entries via PR in that repo.

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.
this repo. Repos tracking `@main` pick up the change on their next scheduled
run; repos pinned to `@v1` pick it up only after the `v1` tag is updated and
then on their next scheduled run. The source list is repo-local and propagates
only within the repo that owns it.

#### Adopting in a new repo

Expand All @@ -485,11 +498,15 @@ this repo and every adopter picks up the change on their next scheduled run.
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
3. (Optional) Copy [`standards/feature-ideation-sources.md`](feature-ideation-sources.md)
to `.github/feature-ideation-sources.md` in the target repo and customise
it for your project. Mary reads YOUR copy — not the central template — so
each repo controls its own source list.
4. (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
5. 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.
6. Confirm the org-level secret `CLAUDE_CODE_OAUTH_TOKEN` is accessible.

#### Critical gotchas (baked into the reusable workflow)

Expand Down
Loading
Loading