feat(templates): add optional Business Context section to PROJECT.md template (#756)
* feat(templates): add optional Business Context section to PROJECT.md template Adds an optional `## Business Context` section (Customer, Revenue model, Success metric, Strategy notes) between Core Value and Requirements, for monetized or customer-facing projects. Optional by default — an HTML comment tells non-business projects to delete it; capped at four one-line fields to stay a constraint reference, not a business plan. The milestone evolution review in complete-milestone.md checks it only when the section is present. Refs #72 * chore(changeset): set pr number for #72 fragment * test(#72): add source-text-is-the-product exemption marker Addresses review Minor #1 on PR #756. The contract test reads the PROJECT.md template and complete-milestone workflow .md files and asserts on their content (the local/no-source-grep pattern). Those .md files ARE the product surface, so this is a valid source-text-is-the-product case. Add the explicit // allow-test-rule marker per RULESET.TESTS.no-source-grep.exemption so intent is audit-traceable before the rule promotes to error (#453). --------- Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
5
.changeset/72-project-business-context.md
Normal file
5
.changeset/72-project-business-context.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
type: Added
|
||||||
|
pr: 756
|
||||||
|
---
|
||||||
|
**Optional `## Business Context` section in the PROJECT.md template** — a four-field block (Customer, Revenue model, Success metric, Strategy notes) for monetized or customer-facing projects, positioned between Core Value and Requirements. Optional by default (an HTML comment tells non-business projects to delete it), capped at four one-line fields to stay a constraint reference rather than a business plan, and reviewed at each milestone by `/gsd-complete-milestone` when present. (#72)
|
||||||
@@ -51,6 +51,8 @@ The `.planning/` directory is GSD Core's shared memory for a project. Every work
|
|||||||
| **Produced by** | `/gsd-new-project` (initial creation); updated by `/gsd-complete-milestone` as decisions are validated. |
|
| **Produced by** | `/gsd-new-project` (initial creation); updated by `/gsd-complete-milestone` as decisions are validated. |
|
||||||
| **Consumed by** | All planning workflows; `gsd-phase-researcher`, `gsd-planner` (context); `discuss-phase` (prior decisions); `gsd-plan-checker` (project constraints). |
|
| **Consumed by** | All planning workflows; `gsd-phase-researcher`, `gsd-planner` (context); `discuss-phase` (prior decisions); `gsd-plan-checker` (project constraints). |
|
||||||
|
|
||||||
|
Includes an optional `## Business Context` section (Customer, Revenue model, Success metric, Strategy notes) for monetized or customer-facing projects — four one-line fields that connect business outcomes to requirement prioritization. It is deleted for internal tools, experiments, or meta workspaces, and reviewed at each milestone by `/gsd-complete-milestone` when present.
|
||||||
|
|
||||||
### `ROADMAP.md`
|
### `ROADMAP.md`
|
||||||
|
|
||||||
| | |
|
| | |
|
||||||
|
|||||||
@@ -17,6 +17,15 @@ Use the user's language and framing. Update whenever reality drifts from this de
|
|||||||
[The ONE thing that matters most. If everything else fails, this must work.
|
[The ONE thing that matters most. If everything else fails, this must work.
|
||||||
One sentence that drives prioritization when tradeoffs arise.]
|
One sentence that drives prioritization when tradeoffs arise.]
|
||||||
|
|
||||||
|
## Business Context
|
||||||
|
|
||||||
|
<!-- OPTIONAL — only for monetized or customer-facing projects. Delete this section otherwise. -->
|
||||||
|
|
||||||
|
- **Customer**: [Who pays / who uses — one line]
|
||||||
|
- **Revenue model**: [How it makes money — one line]
|
||||||
|
- **Success metric**: [The number that matters — one line]
|
||||||
|
- **Strategy notes**: [Link to external strategy doc, if any]
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
### Validated
|
### Validated
|
||||||
@@ -83,6 +92,13 @@ Common types: Tech stack, Timeline, Budget, Dependencies, Compatibility, Perform
|
|||||||
- Drives prioritization when tradeoffs arise
|
- Drives prioritization when tradeoffs arise
|
||||||
- Rarely changes; if it does, it's a significant pivot
|
- Rarely changes; if it does, it's a significant pivot
|
||||||
|
|
||||||
|
**Business Context:**
|
||||||
|
- Optional — only for monetized or customer-facing projects
|
||||||
|
- Delete the entire section for internal tools, experiments, or meta workspaces
|
||||||
|
- 4 fields max, one line each — a constraint reference, not a business plan
|
||||||
|
- Use **Strategy notes** to link out to a dedicated strategy doc rather than duplicating it here
|
||||||
|
- Informs requirement prioritization: features serving the customer/revenue model come first
|
||||||
|
|
||||||
**Requirements — Validated:**
|
**Requirements — Validated:**
|
||||||
- Requirements that shipped and proved valuable
|
- Requirements that shipped and proved valuable
|
||||||
- Format: `- ✓ [Requirement] — [version/phase]`
|
- Format: `- ✓ [Requirement] — [version/phase]`
|
||||||
@@ -140,8 +156,9 @@ and implemented by workflows/transition.md and workflows/complete-milestone.md.
|
|||||||
**After each milestone:**
|
**After each milestone:**
|
||||||
1. Full review of all sections
|
1. Full review of all sections
|
||||||
2. Core Value check — still the right priority?
|
2. Core Value check — still the right priority?
|
||||||
3. Audit Out of Scope — reasons still valid?
|
3. Business Context check (if present) — customer, revenue model, success metric still accurate?
|
||||||
4. Update Context with current state (users, feedback, metrics)
|
4. Audit Out of Scope — reasons still valid?
|
||||||
|
5. Update Context with current state (users, feedback, metrics)
|
||||||
|
|
||||||
</evolution>
|
</evolution>
|
||||||
|
|
||||||
|
|||||||
@@ -245,7 +245,12 @@ cat .planning/phases/*-*/*-SUMMARY.md
|
|||||||
- Still the right priority? Did shipping reveal a different core value?
|
- Still the right priority? Did shipping reveal a different core value?
|
||||||
- Update if the ONE thing has shifted
|
- Update if the ONE thing has shifted
|
||||||
|
|
||||||
3. **Requirements audit:**
|
3. **Business Context check (only if the section is present):**
|
||||||
|
- Skip entirely if PROJECT.md has no `## Business Context` section
|
||||||
|
- Customer, revenue model, and success metric still accurate after shipping?
|
||||||
|
- Update any field that drifted; refresh the linked strategy doc reference if it moved
|
||||||
|
|
||||||
|
4. **Requirements audit:**
|
||||||
|
|
||||||
**Validated section:**
|
**Validated section:**
|
||||||
- All Active requirements shipped this milestone → Move to Validated
|
- All Active requirements shipped this milestone → Move to Validated
|
||||||
@@ -261,17 +266,17 @@ cat .planning/phases/*-*/*-SUMMARY.md
|
|||||||
- Remove irrelevant items
|
- Remove irrelevant items
|
||||||
- Add requirements invalidated during milestone
|
- Add requirements invalidated during milestone
|
||||||
|
|
||||||
4. **Context update:**
|
5. **Context update:**
|
||||||
- Current codebase state (LOC, tech stack)
|
- Current codebase state (LOC, tech stack)
|
||||||
- User feedback themes (if any)
|
- User feedback themes (if any)
|
||||||
- Known issues or technical debt
|
- Known issues or technical debt
|
||||||
|
|
||||||
5. **Key Decisions audit:**
|
6. **Key Decisions audit:**
|
||||||
- Extract all decisions from milestone phase summaries
|
- Extract all decisions from milestone phase summaries
|
||||||
- Add to Key Decisions table with outcomes
|
- Add to Key Decisions table with outcomes
|
||||||
- Mark ✓ Good, ⚠️ Revisit, or — Pending
|
- Mark ✓ Good, ⚠️ Revisit, or — Pending
|
||||||
|
|
||||||
6. **Constraints check:**
|
7. **Constraints check:**
|
||||||
- Any constraints changed during development? Update as needed
|
- Any constraints changed during development? Update as needed
|
||||||
|
|
||||||
Update PROJECT.md inline. Update "Last updated" footer:
|
Update PROJECT.md inline. Update "Last updated" footer:
|
||||||
@@ -355,6 +360,7 @@ Initial user testing showed demand for shape tools.
|
|||||||
|
|
||||||
- [ ] "What This Is" reviewed and updated if needed
|
- [ ] "What This Is" reviewed and updated if needed
|
||||||
- [ ] Core Value verified as still correct
|
- [ ] Core Value verified as still correct
|
||||||
|
- [ ] Business Context checked (or confirmed absent)
|
||||||
- [ ] All shipped requirements moved to Validated
|
- [ ] All shipped requirements moved to Validated
|
||||||
- [ ] New requirements added to Active for next milestone
|
- [ ] New requirements added to Active for next milestone
|
||||||
- [ ] Out of Scope reasoning audited
|
- [ ] Out of Scope reasoning audited
|
||||||
|
|||||||
96
tests/enh-72-business-context.test.cjs
Normal file
96
tests/enh-72-business-context.test.cjs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
// allow-test-rule: source-text-is-the-product
|
||||||
|
// The PROJECT.md template + complete-milestone workflow .md ARE the product surface
|
||||||
|
// the runtime loads; asserting on their text tests the deployed contract directly.
|
||||||
|
/**
|
||||||
|
* Enhancement #72 — optional Business Context section in the PROJECT.md template.
|
||||||
|
*
|
||||||
|
* Contract tests over the product-text surfaces (template + milestone workflow .md):
|
||||||
|
* the template offers a Business Context section that is explicitly OPTIONAL, capped
|
||||||
|
* at the four approved one-line fields, and the milestone evolution review treats it
|
||||||
|
* as conditional so non-business projects that deleted it are never forced to review it.
|
||||||
|
*/
|
||||||
|
const { test, describe } = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const TEMPLATE = path.join(__dirname, '..', 'gsd-core', 'templates', 'project.md');
|
||||||
|
const COMPLETE_MILESTONE = path.join(__dirname, '..', 'gsd-core', 'workflows', 'complete-milestone.md');
|
||||||
|
|
||||||
|
function parseTemplateContract(content) {
|
||||||
|
const lines = content.split(/\r?\n/);
|
||||||
|
const lower = content.toLowerCase();
|
||||||
|
// The Business Context block lives between its heading and the next "## " heading.
|
||||||
|
const startIdx = lines.findIndex(l => l.trim() === '## Business Context');
|
||||||
|
let sectionBody = '';
|
||||||
|
if (startIdx !== -1) {
|
||||||
|
const rest = lines.slice(startIdx + 1);
|
||||||
|
const endOffset = rest.findIndex(l => l.startsWith('## '));
|
||||||
|
sectionBody = (endOffset === -1 ? rest : rest.slice(0, endOffset)).join('\n');
|
||||||
|
}
|
||||||
|
const fieldOf = (label) => new RegExp(`^- \\*\\*${label}\\*\\*:`, 'm').test(sectionBody);
|
||||||
|
return {
|
||||||
|
hasSection: startIdx !== -1,
|
||||||
|
// Optional-by-default: an HTML comment tells non-business projects to delete it.
|
||||||
|
hasOptionalMarker: /<!--\s*OPTIONAL/i.test(sectionBody) && /delete this section/i.test(sectionBody),
|
||||||
|
fields: {
|
||||||
|
customer: fieldOf('Customer'),
|
||||||
|
revenueModel: fieldOf('Revenue model'),
|
||||||
|
successMetric: fieldOf('Success metric'),
|
||||||
|
strategyNotes: fieldOf('Strategy notes'),
|
||||||
|
},
|
||||||
|
fieldCount: (sectionBody.match(/^- \*\*/gm) || []).length,
|
||||||
|
// Positioned between Core Value and Requirements.
|
||||||
|
orderedBetweenCoreValueAndRequirements:
|
||||||
|
lower.indexOf('## core value') < lower.indexOf('## business context') &&
|
||||||
|
lower.indexOf('## business context') < lower.indexOf('## requirements'),
|
||||||
|
hasGuidelinesEntry: /\*\*Business Context:\*\*/.test(content),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseMilestoneContract(content) {
|
||||||
|
const lower = content.toLowerCase();
|
||||||
|
const lines = content.split(/\r?\n/);
|
||||||
|
const reviewLine = lines.find(l =>
|
||||||
|
l.toLowerCase().includes('business context') &&
|
||||||
|
(l.toLowerCase().includes('if present') || l.toLowerCase().includes('only if')),
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
mentionsBusinessContext: lower.includes('business context'),
|
||||||
|
hasConditionalReview: Boolean(reviewLine),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('enhancement #72 — Business Context template section', () => {
|
||||||
|
const tpl = parseTemplateContract(fs.readFileSync(TEMPLATE, 'utf-8'));
|
||||||
|
|
||||||
|
test('template includes a Business Context section', () => {
|
||||||
|
assert.ok(tpl.hasSection, 'template must contain a "## Business Context" section');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('section is marked OPTIONAL with delete-for-non-business guidance', () => {
|
||||||
|
assert.ok(tpl.hasOptionalMarker, 'section must carry an OPTIONAL HTML comment telling non-business projects to delete it');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('section carries exactly the four approved one-line fields', () => {
|
||||||
|
assert.ok(tpl.fields.customer, 'missing **Customer** field');
|
||||||
|
assert.ok(tpl.fields.revenueModel, 'missing **Revenue model** field');
|
||||||
|
assert.ok(tpl.fields.successMetric, 'missing **Success metric** field');
|
||||||
|
assert.ok(tpl.fields.strategyNotes, 'missing **Strategy notes** field');
|
||||||
|
assert.strictEqual(tpl.fieldCount, 4, 'section is capped at four fields (constraint reference, not a business plan)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('section is positioned between Core Value and Requirements', () => {
|
||||||
|
assert.ok(tpl.orderedBetweenCoreValueAndRequirements, 'Business Context must sit between Core Value and Requirements');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('guidelines document the Business Context section', () => {
|
||||||
|
assert.ok(tpl.hasGuidelinesEntry, 'guidelines block must include a **Business Context:** entry');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('milestone evolution reviews Business Context only when present', () => {
|
||||||
|
const ms = parseMilestoneContract(fs.readFileSync(COMPLETE_MILESTONE, 'utf-8'));
|
||||||
|
assert.ok(ms.mentionsBusinessContext, 'complete-milestone must mention Business Context in its review');
|
||||||
|
assert.ok(ms.hasConditionalReview, 'the Business Context milestone review must be conditional on the section being present');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user