Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
45 changes: 45 additions & 0 deletions .github/ISSUE_TEMPLATE/general-issue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
name: General Issue
about: Report a bug, request a feature, or propose a change
title: ""
labels: []
assignees: ""
---

## Summary

{Brief description of the issue, feature, or change}

## Plan Approval

<!--
Before implementation begins, post a plan comment on this issue:
1. Create plan file at docs/plans/YYYY-MM-DD-issue-N-description.md (if complex)
2. Post comment with "## Plan" header summarizing implementation approach
3. Wait for approval comment before starting implementation
4. See Issue #177 for exemplar

Plan comment template:
## Plan
**Approach:** {Brief description}
**Files to change:** {List}
**Testing:** {How you'll verify}
**Awaiting approval to proceed.**
-->

- [ ] Plan comment posted
- [ ] Approval received

## Context

{Background information, motivation, or problem statement}

## Acceptance Criteria

- [ ] {Criterion 1}
- [ ] {Criterion 2}
- [ ] {Criterion 3}

## Additional Information

{Any other relevant details, links, or screenshots}
20 changes: 20 additions & 0 deletions .github/ISSUE_TEMPLATE/skill-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,26 @@ assignees: ""

{One sentence: what agents should do when this skill triggers}

## Plan Approval

<!--
Before implementation begins, post a plan comment on this issue:
1. Create plan file at docs/plans/YYYY-MM-DD-issue-N-description.md (if complex)
2. Post comment with "## Plan" header summarizing implementation approach
3. Wait for approval comment before starting implementation
4. See Issue #177 for exemplar

Plan comment template:
## Plan
**Approach:** {Brief description}
**Files to change:** {List}
**Testing:** {How you'll verify}
**Awaiting approval to proceed.**
-->

- [ ] Plan comment posted
- [ ] Approval received

## Skill Priority

- Priority: P{0-4} ({Safety & Integrity | Quality & Correctness | Consistency & Governance |
Expand Down
24 changes: 24 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,30 @@ agent-facing documentation that helps AI agents apply proven techniques, pattern
- See [README.md](README.md) for repository standards
- See [AGENTS.md](AGENTS.md) for agent-specific rules

## Plan Approval Workflow

Before implementation begins on any issue, a plan must be posted and approved:

1. **Post plan comment** on the issue with "## Plan" header summarizing approach
2. **Wait for approval** - approval comment must contain "Approval acknowledged" or "Plan approved"
3. **Begin implementation** - only after explicit approval exists

**Enforcement:** DangerJS Rule 9 warns on PRs where the linked issue lacks plan approval.

**Exemplar:** [Issue #177](https://github.com/mcj-coder/development-skills/issues/177) demonstrates
the complete workflow with proper plan formatting and approval.

**Plan comment format:**

```markdown
## Plan

**Approach:** {Brief description}
**Files to change:** {List}
**Testing:** {How you'll verify}
**Awaiting approval to proceed.**
```

## Creating Skills

### Process Overview
Expand Down
108 changes: 100 additions & 8 deletions dangerfile.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// dangerfile.js - Evidence validation rules for PR enforcement
// Implements automated checks from issue-driven-delivery skill
// Issue: #300
// Issues: #300, #290, #291, #292

const { danger, warn, fail, message } = require("danger");

Expand Down Expand Up @@ -48,22 +48,25 @@ function findCheckedItemsWithoutEvidence(text) {
return itemsWithoutEvidence;
}

// Helper to safely get issue body (linked issue from PR)
async function getLinkedIssueBody() {
// Helper to extract linked issue number from PR body
function getLinkedIssueNumber() {
const prBody = danger.github.pr.body || "";

// Extract issue number from "Closes #N" or "Fixes #N" patterns
const issueMatch = prBody.match(/(?:closes|fixes|resolves)\s+#(\d+)/i);
if (!issueMatch) {
return issueMatch ? parseInt(issueMatch[1]) : null;
}

// Helper to safely get issue body (linked issue from PR)
async function getLinkedIssueBody() {
const issueNumber = getLinkedIssueNumber();
if (!issueNumber) {
return null;
}

const issueNumber = issueMatch[1];
try {
const issue = await danger.github.api.issues.get({
owner: danger.github.thisPR.owner,
repo: danger.github.thisPR.repo,
issue_number: parseInt(issueNumber),
issue_number: issueNumber,
});
return issue.data.body || "";
} catch (error) {
Expand All @@ -80,6 +83,72 @@ async function getLinkedIssueBody() {
}
}

// Helper to get comments on the linked issue
async function getLinkedIssueComments() {
const issueNumber = getLinkedIssueNumber();
if (!issueNumber) {
return null;
}

try {
const comments = await danger.github.api.issues.listComments({
owner: danger.github.thisPR.owner,
repo: danger.github.thisPR.repo,
issue_number: issueNumber,
});
return comments.data || [];
} catch (error) {
console.error(
`Failed to fetch comments for issue #${issueNumber}: ${error.message || error}`,
);
return null;
}
}

/**
* Check if a comment contains a plan (implementation plan for approval)
* Plan indicators:
* - Contains "## Plan" or "## Implementation Plan" or "## Refinement"
* - Links to docs/plans/ directory
* - Contains "awaiting approval" or "ready for approval"
*/
function isPlanComment(commentBody) {
if (!commentBody) return false;
const lowerBody = commentBody.toLowerCase();

// Check for plan headers
if (/##\s*(implementation\s+)?plan/i.test(commentBody)) return true;
if (/##\s*refinement/i.test(commentBody)) return true;

// Check for plan file links
if (/docs\/plans\//.test(commentBody)) return true;

// Check for approval request language
if (
lowerBody.includes("awaiting approval") ||
lowerBody.includes("ready for approval") ||
lowerBody.includes("plan ready for approval")
)
return true;

return false;
}

/**
* Check if a comment indicates plan approval
*/
function isApprovalComment(commentBody) {
if (!commentBody) return false;
const lowerBody = commentBody.toLowerCase();

return (
lowerBody.includes("approval acknowledged") ||
lowerBody.includes("approved to proceed") ||
lowerBody.includes("plan approved") ||
/proceed(ing)?\s+with/.test(lowerBody)
);
}

// Main validation logic
async function validate() {
const prBody = danger.github.pr.body || "";
Expand Down Expand Up @@ -214,6 +283,29 @@ async function validate() {
}
}

// Rule 9: Plan approval enforcement
// Issues must have a plan comment with approval before implementation
const issueComments = await getLinkedIssueComments();
if (issueComments) {
const hasPlanComment = issueComments.some((c) => isPlanComment(c.body));
const hasApprovalComment = issueComments.some((c) =>
isApprovalComment(c.body),
);

if (!hasPlanComment) {
warn(
`[Issue] No plan comment found on linked issue. ` +
`Post a plan comment with "## Plan" header or link to docs/plans/ before implementation. ` +
`See Issue #177 for exemplar.`,
);
} else if (!hasApprovalComment) {
warn(
`[Issue] Plan found but no approval comment detected. ` +
`Add approval comment (e.g., "Approval acknowledged" or "Plan approved") before implementation.`,
);
}
}

// Success message if all critical checks pass
const failures = danger.fails || [];
if (failures.length === 0) {
Expand Down
18 changes: 18 additions & 0 deletions skills/issue-driven-delivery/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,24 @@ gh issue view N --json comments --jq '.comments[].body' | grep -qiE "approved|lg
gh issue view N --json body,comments --jq '[.body, .comments[].body] | join(" ")' | grep -qiE "blocked by|depends on|no dependencies|dependencies: none" && echo "PASS: Dependencies documented" || echo "WARN: Dependencies not explicitly documented"
```

### Automated Enforcement

Plan approval is enforced automatically via DangerJS (Rule 9). When a PR references an issue:

1. **Plan comment check** - DangerJS verifies the linked issue has a plan comment containing:
- "## Plan" or "## Implementation Plan" or "## Refinement" header
- Link to `docs/plans/` directory
- "Awaiting approval" or "Ready for approval" language

2. **Approval check** - DangerJS verifies an approval comment exists containing:
- "Approval acknowledged" or "Plan approved"
- "Approved to proceed" or "Proceeding with"

PRs will receive warnings if plan approval is missing. See `dangerfile.js` Rule 9 for implementation.

**Exemplar:** [Issue #177](https://github.com/mcj-coder/development-skills/issues/177) demonstrates
the complete plan approval workflow with proper comment formatting.

### DoR Failure Handling

If DoR validation fails, do NOT transition to implementation. Instead:
Expand Down
Loading