Why
Today forge-loop's PO expands whatever issue you label loop:ready. It has no opinion about whether the ticket should exist. The result: backlogs drift toward cosmetic features (ETag headers, sparklines, theme polish) because anyone can file anything and the loop will ship it.
For paid customer use, every project running forge-loop needs a Product Brainstormer / PO Master that is opinionated about what work matters — and that opinion is tunable per project.
What
A first-class forge-loop feature: each project defines its own product vision + product axes in .forge/. The brainstormer reads them before proposing ANY ticket, fills epics aligned with the axes, dispatches tickets under those epics, and refuses cosmetic work by an explicit rubric.
Customer story
A platform lead drops .forge/product-vision.md + .forge/axes.yaml into their repo. They write 5 sentences about who their customer is and 4-6 named value axes. forge-loop brainstorm reads it, files epics, populates tickets under those epics. The iteration loop picks them up. Cosmetic tickets the team accidentally files get demoted to loop:cold automatically.
Files customers own
.forge/product-vision.md
Free-form. Names the customer, the golden path, the wedge ("what makes this product valuable to a paying user"). The brainstormer quotes from it in every ticket it files (so reviewers can audit alignment).
.forge/axes.yaml
Structured. Each axis: name, customer, "valuable means", acceptable_work, rejected_as_cosmetic. The brainstormer scores every proposed ticket against the rubric — must move at least one axis, must not match the rejected list.
axes:
- name: golden-path-e2e
customer: "SRE deploying the product for the first time"
valuable_means: "...customer-shaped Playwright fixtures..."
acceptable_work:
- "Playwright tests against the real rig"
- "Adversarial paths: failed step, OOM step, secret leak"
rejected_as_cosmetic:
- "Pagination on endpoints customers don't poll"
- "Visual polish without a UX defect"
- name: scm-depth
...
Acceptance
Sub-tickets (this epic's children)
Filed in this PR as separate issues:
.forge/product-vision.md + .forge/axes.yaml discovery + pydantic schema
- Brainstormer skill module (
forge_loop/brainstormer.py) — reads vision + axes, fills epics + tickets
forge-loop brainstorm CLI command (dry-run + --apply)
- Periodic backlog audit (demote cosmetic tickets to
loop:cold)
- Per-axis label namespace + axis-aware sort/filter in
forge-loop status
NOT in scope
- The product vision content itself (each customer writes their own)
- The Titan-specific axes (lives in
dashboard-plugin/.forge/product-vision.md as the reference example, NOT in forge-loop core)
Why
Today forge-loop's PO expands whatever issue you label
loop:ready. It has no opinion about whether the ticket should exist. The result: backlogs drift toward cosmetic features (ETag headers, sparklines, theme polish) because anyone can file anything and the loop will ship it.For paid customer use, every project running forge-loop needs a Product Brainstormer / PO Master that is opinionated about what work matters — and that opinion is tunable per project.
What
A first-class forge-loop feature: each project defines its own product vision + product axes in
.forge/. The brainstormer reads them before proposing ANY ticket, fills epics aligned with the axes, dispatches tickets under those epics, and refuses cosmetic work by an explicit rubric.Customer story
Files customers own
.forge/product-vision.mdFree-form. Names the customer, the golden path, the wedge ("what makes this product valuable to a paying user"). The brainstormer quotes from it in every ticket it files (so reviewers can audit alignment).
.forge/axes.yamlStructured. Each axis: name, customer, "valuable means", acceptable_work, rejected_as_cosmetic. The brainstormer scores every proposed ticket against the rubric — must move at least one axis, must not match the rejected list.
Acceptance
.forge/product-vision.mdis auto-discovered at brainstormer-start. Missing file = brainstormer refuses to file tickets, emits a clear "no vision file" error..forge/axes.yamlis auto-discovered with a schema (pydantic). Missing/invalid = same hard refusal.forge-loop brainstormCLI command runs the brainstormer once and prints proposed epics/tickets without filing them (dry-run by default).--applyfiles them on GitHub.epic+axis:<name>) and tickets under each epic (issues labeledloop:ready+axis:<name>+ epic cross-link). Tickets without an axis label are anti-pattern.loop:readytickets and demotes ones that don't match any axis toloop:cold+ comments why.Sub-tickets (this epic's children)
Filed in this PR as separate issues:
.forge/product-vision.md+.forge/axes.yamldiscovery + pydantic schemaforge_loop/brainstormer.py) — reads vision + axes, fills epics + ticketsforge-loop brainstormCLI command (dry-run +--apply)loop:cold)forge-loop statusNOT in scope
dashboard-plugin/.forge/product-vision.mdas the reference example, NOT in forge-loop core)