diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml new file mode 100644 index 00000000..a86a1217 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement.yml @@ -0,0 +1,160 @@ +--- +name: Enhancement Proposal +description: Propose an improvement to an existing feature. Read the full instructions before opening this issue. +labels: ["enhancement", "needs-review"] +body: + - type: markdown + attributes: + value: | + ## ⚠️ Read this before you fill anything out + + An enhancement improves something that already exists — better output, expanded edge-case handling, improved performance, cleaner UX. It does **not** add new commands, new workflows, or new concepts. If you are proposing something new, use the [Feature Request](./feature_request.yml) template instead. + + **Before opening this issue:** + - Confirm the thing you want to improve actually exists and works today. + - Read [CONTRIBUTING.md](../../CONTRIBUTING.md#-enhancement) — understand what `approved-enhancement` means and why you must wait for it before writing any code. + + **What happens after you submit:** + A maintainer will review this proposal. If it is incomplete or out of scope, it will be **closed**. If approved, it will be labeled `approved-enhancement` and you may begin coding. + + **Do not open a PR until this issue is labeled `approved-enhancement`.** + + - type: checkboxes + id: preflight + attributes: + label: Pre-submission checklist + description: You must check every box. Unchecked boxes are an immediate close. + options: + - label: I have confirmed this improves existing behavior — it does not add a new command, workflow, or concept + required: true + - label: I have searched existing issues and this enhancement has not already been proposed + required: true + - label: I have read CONTRIBUTING.md and understand I must wait for `approved-enhancement` before writing any code + required: true + - label: I can clearly describe the concrete benefit — not just "it would be nicer" + required: true + + - type: input + id: what_is_being_improved + attributes: + label: What existing feature or behavior does this improve? + description: Name the specific command, workflow, output, or behavior you are enhancing. + placeholder: "e.g., `/gsd:plan` output, phase status display in statusline, context summary format" + validations: + required: true + + - type: textarea + id: current_behavior + attributes: + label: Current behavior + description: | + Describe exactly how the thing works today. Be specific. Include example output or commands if helpful. + placeholder: | + Currently, `/gsd:status` shows: + ``` + Phase 2/5 — In Progress + ``` + It does not show the phase name, making it hard to know what phase you are actually in without + opening STATE.md. + validations: + required: true + + - type: textarea + id: proposed_behavior + attributes: + label: Proposed behavior + description: | + Describe exactly how it should work after the enhancement. Be specific. Include example output or commands. + placeholder: | + After the enhancement, `/gsd:status` would show: + ``` + Phase 2/5 — In Progress — "Implement core auth module" + ``` + The phase name is pulled from STATE.md and appended to the existing output. + validations: + required: true + + - type: textarea + id: reason_and_benefit + attributes: + label: Reason and benefit + description: | + Answer both of these clearly: + + 1. **Why is the current behavior a problem?** (Not just inconvenient — what goes wrong, what is harder than it should be, or what is confusing?) + 2. **What is the concrete benefit of the proposed behavior?** (What becomes easier, faster, less error-prone, or clearer?) + + Vague answers like "it would be better" or "it's more user-friendly" are not sufficient. + placeholder: | + **Why the current behavior is a problem:** + When working in a long session, the AI agent frequently loses track of which phase is active + and must re-read STATE.md. The numeric-only status gives no semantic context. + + **Concrete benefit:** + Showing the phase name means the agent can confirm the active phase from the status output + alone, without an extra file read. This reduces context consumption in long sessions. + validations: + required: true + + - type: textarea + id: scope + attributes: + label: Scope of changes + description: | + List the files and systems this enhancement would touch. Be complete. + An enhancement should have a narrow, well-defined scope. If your list is long, this might be a feature, not an enhancement. + placeholder: | + Files modified: + - `get-shit-done/commands/gsd/status.md` — update output format description + - `get-shit-done/bin/lib/state.cjs` — expose phase name in status() return value + - `tests/status.test.cjs` — update snapshot and add test for phase name in output + - `CHANGELOG.md` — user-facing change entry + + No new files created. No new dependencies. + validations: + required: true + + - type: textarea + id: breaking_changes + attributes: + label: Breaking changes + description: | + Does this change existing command output, file formats, or behavior that users or AI agents might depend on? + If yes, describe exactly what changes and how it stays backward compatible (or why it cannot). + Write "None" only if you are certain. + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: | + What other ways could this be improved? Why is your proposed approach the right one? + If you haven't considered alternatives, take a moment before submitting. + validations: + required: true + + - type: dropdown + id: area + attributes: + label: Area affected + options: + - Core workflow (init, plan, build, verify) + - Planning system (phases, roadmap, state) + - Context management (context engineering, summaries) + - Runtime integration (hooks, statusline, settings) + - Installation / setup + - Output / formatting + - Documentation + - Other + validations: + required: true + + - type: textarea + id: additional_context + attributes: + label: Additional context + description: Screenshots, related issues, or anything else that helps explain the proposal. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 3a928de7..e6d99957 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,44 +1,165 @@ --- name: Feature Request -description: Suggest a new feature or improvement -labels: ["enhancement"] +description: Propose a new feature. Read the full instructions before opening this issue. +labels: ["feature-request", "needs-review"] body: - type: markdown attributes: value: | - Thanks for suggesting a feature! Please describe what you'd like to see. + ## ⚠️ Read this before you fill anything out - - type: textarea - id: problem + A feature adds something new to GSD — a new command, workflow, concept, or integration. Features have the **highest bar** for acceptance because every feature adds permanent maintenance burden to a project built for solo developers. + + **Before opening this issue:** + - Check [Discussions](https://github.com/gsd-build/get-shit-done/discussions) — has this been proposed and declined before? + - Read [CONTRIBUTING.md](../../CONTRIBUTING.md#-feature) — understand what "approved-feature" means and why you must wait for it before writing code. + - Ask yourself: *does this solve a real problem for a solo developer working with an AI coding tool, or is it a feature I personally want?* + + **What happens after you submit:** + A maintainer will review this spec. If it is incomplete, it will be **closed**, not revised. If it conflicts with GSD's design philosophy, it will be declined. If it is approved, it will be labeled `approved-feature` and you may begin coding. + + **Do not open a PR until this issue is labeled `approved-feature`.** + + - type: checkboxes + id: preflight attributes: - label: Problem or motivation - description: What problem does this solve? Why do you want this? - placeholder: "I'm frustrated when..." + label: Pre-submission checklist + description: You must check every box. Unchecked boxes are an immediate close. + options: + - label: I have searched existing issues and discussions — this has not been proposed and declined before + required: true + - label: I have read CONTRIBUTING.md and understand that I must wait for `approved-feature` before writing any code + required: true + - label: I have read the existing GSD commands and workflows and confirmed this feature does not duplicate existing behavior + required: true + - label: This feature solves a problem for solo developers using AI coding tools, not a personal preference or workflow I happen to like + required: true + + - type: input + id: feature_name + attributes: + label: Feature name + description: A short, concrete name for this feature (not a sales pitch — just what it is). + placeholder: "e.g., Phase rollback command, Auto-archive completed phases, Cross-project state sync" + validations: + required: true + + - type: dropdown + id: feature_type + attributes: + label: Type of addition + description: What kind of thing is this feature adding? + options: + - New command (slash command or CLI flag) + - New workflow (multi-step process) + - New runtime integration + - New planning concept (phase type, state, etc.) + - New installation/setup behavior + - New output or reporting format + - Other (describe in spec) validations: required: true - type: textarea - id: solution + id: problem_statement attributes: - label: Proposed solution - description: How do you think this should work? Include example commands or workflows if possible. + label: The solo developer problem + description: | + Describe the concrete problem this solves for a solo developer using an AI coding tool. Be specific. + + Good: "When a phase fails mid-way, there is no way to roll back state without manually editing STATE.md. This causes the AI agent to continue from a corrupted state, producing wrong plans." + + Bad: "It would be nice to have a rollback feature." / "Other tools have this." / "I need this for my workflow." placeholder: | - A new command `/gsd:example` that... + When [specific situation], the developer cannot [specific thing], which causes [specific negative outcome]. + validations: + required: true + + - type: textarea + id: what_is_added + attributes: + label: What this feature adds + description: | + Describe exactly what is being added. Be specific about commands, output, behavior, and user interaction. + Include example commands or example output where possible. + placeholder: | + A new command `/gsd:rollback` that: + 1. Reads the current phase from STATE.md + 2. Reverts STATE.md to the previous phase's snapshot + 3. Outputs a confirmation with the rolled-back state + + Example usage: + ``` + /gsd:rollback + > Rolled back from Phase 3 (failed) to Phase 2 (completed) + ``` + validations: + required: true + + - type: textarea + id: full_scope + attributes: + label: Full scope of changes + description: | + List every file, system, and area of the codebase this feature would touch. Be exhaustive. + If you cannot fill this out, you do not understand the codebase well enough to propose this feature yet. + placeholder: | + Files that would be created: + - `get-shit-done/commands/gsd/rollback.md` — new slash command definition + + Files that would be modified: + - `get-shit-done/bin/lib/state.cjs` — add rollback() function + - `get-shit-done/bin/lib/phases.cjs` — expose phase snapshot API + - `tests/rollback.test.cjs` — new test file + - `docs/COMMANDS.md` — document new command + - `CHANGELOG.md` — entry for this feature + + Systems affected: + - STATE.md schema (must remain backward compatible) + - Phase lifecycle state machine + validations: + required: true + + - type: textarea + id: user_stories + attributes: + label: User stories + description: Write at least two user stories in the format "As a [user], I want [thing] so that [outcome]." + placeholder: | + 1. As a solo developer, I want to roll back a failed phase so that I can re-attempt it without corrupting my project state. + 2. As a solo developer, I want rollback to be undoable so that I don't accidentally lose completed work. + validations: + required: true + + - type: textarea + id: acceptance_criteria + attributes: + label: Acceptance criteria + description: | + List the specific, testable conditions that must be true for this feature to be considered complete. + These become the basis for reviewer sign-off. Vague criteria ("it works") are not acceptable. + placeholder: | + - [ ] `/gsd:rollback` reverts STATE.md to the previous phase when current phase status is `failed` + - [ ] `/gsd:rollback` exits with an error if there is no previous phase to roll back to + - [ ] `/gsd:rollback` outputs the before/after phase names in its confirmation message + - [ ] Rollback is logged in the phase history so the AI agent can see it happened + - [ ] All existing tests still pass + - [ ] New tests cover the happy path, no-previous-phase case, and STATE.md corruption case validations: required: true - type: dropdown id: scope attributes: - label: Which area does this affect? + label: Which area does this primarily affect? options: - Core workflow (init, plan, build, verify) - Planning system (phases, roadmap, state) - Context management (context engineering, summaries) - Runtime integration (hooks, statusline, settings) - Installation / setup - - Documentation - - Other + - Documentation only + - Multiple areas (describe in scope section above) validations: required: true @@ -46,7 +167,7 @@ body: id: runtimes attributes: label: Applicable runtimes - description: Which runtimes should this work with? + description: Which runtimes must this work with? Check all that apply. options: - label: Claude Code - label: Gemini CLI @@ -58,18 +179,72 @@ body: - label: Windsurf - label: All runtimes + - type: textarea + id: breaking_changes + attributes: + label: Breaking changes assessment + description: | + Does this feature change existing behavior, command output, file formats, or APIs? + If yes, describe exactly what breaks and how existing users would migrate. + Write "None" only if you are certain. + placeholder: | + None — this adds a new command and does not modify any existing command behavior or file schemas. + + OR: + + STATE.md will gain a new `phase_history` array field. Existing STATE.md files without this field + will be treated as having an empty history (backward compatible). The rollback command will + decline gracefully if history is empty. + validations: + required: true + + - type: textarea + id: maintenance_burden + attributes: + label: Maintenance burden + description: | + Every feature is code that must be maintained forever. Describe the ongoing cost: + - How does this interact with future changes to phases, state, or commands? + - Does this add external dependencies? + - Does this require documentation updates across multiple files? + - Will this create edge cases or interactions with other features? + placeholder: | + - No new dependencies + - The rollback function must be updated if the STATE.md schema ever changes + - Will need to be tested on each new Node.js LTS release + - The command definition must be kept in sync with any future command format changes + validations: + required: true + - type: textarea id: alternatives attributes: label: Alternatives considered - description: Have you considered other approaches? + description: | + What other approaches did you consider? Why did you reject them? + If the answer is "I didn't consider any alternatives", this issue will be closed. + placeholder: | + 1. Manual STATE.md editing — rejected because it requires the developer to understand the schema + and is error-prone. The AI agent cannot reliably guide this. + 2. A `/gsd:reset` command that wipes all state — rejected because it is too destructive and + loses all completed phase history. + validations: + required: true + + - type: textarea + id: prior_art + attributes: + label: Prior art and references + description: | + Does any other tool, project, or GSD discussion address this? Link to anything relevant. + If you are aware of a prior declined proposal for this feature, explain why this proposal is different. validations: required: false - type: textarea - id: context + id: additional_context attributes: label: Additional context - description: Any other information, screenshots, or examples. + description: Anything else — screenshots, recordings, related issues, or links. validations: required: false diff --git a/.github/PULL_REQUEST_TEMPLATE/enhancement.md b/.github/PULL_REQUEST_TEMPLATE/enhancement.md new file mode 100644 index 00000000..7825fde6 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/enhancement.md @@ -0,0 +1,86 @@ +## Enhancement PR + +> **Using the wrong template?** +> — Bug fix: use [fix.md](?template=fix.md) +> — New feature: use [feature.md](?template=feature.md) + +--- + +## Linked Issue + +> **Required.** This PR will be auto-closed if no valid issue link is found. +> The linked issue **must** have the `approved-enhancement` label. If it does not, this PR will be closed without review. + +Closes # + +> ⛔ **No `approved-enhancement` label on the issue = immediate close.** +> Do not open this PR if a maintainer has not yet approved the enhancement proposal. + +--- + +## What this enhancement improves + + + +## Before / After + +**Before:** + + +**After:** + + +## How it was implemented + + + +## Testing + +### How I verified the enhancement works + + + +### Platforms tested + +- [ ] macOS +- [ ] Windows (including backslash path handling) +- [ ] Linux +- [ ] N/A (not platform-specific) + +### Runtimes tested + +- [ ] Claude Code +- [ ] Gemini CLI +- [ ] OpenCode +- [ ] Other: ___ +- [ ] N/A (not runtime-specific) + +--- + +## Scope confirmation + + + +- [ ] The implementation matches the scope approved in the linked issue — no additions or removals +- [ ] If scope changed during implementation, I updated the issue and got re-approval before continuing + +--- + +## Checklist + +- [ ] Issue linked above with `Closes #NNN` — **PR will be auto-closed if missing** +- [ ] Linked issue has the `approved-enhancement` label — **PR will be closed if missing** +- [ ] Changes are scoped to the approved enhancement — nothing extra included +- [ ] All existing tests pass (`npm test`) +- [ ] New or updated tests cover the enhanced behavior +- [ ] CHANGELOG.md updated +- [ ] Documentation updated if behavior or output changed +- [ ] No unnecessary dependencies added + +## Breaking changes + + + +None diff --git a/.github/PULL_REQUEST_TEMPLATE/feature.md b/.github/PULL_REQUEST_TEMPLATE/feature.md new file mode 100644 index 00000000..86765c5b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/feature.md @@ -0,0 +1,113 @@ +## Feature PR + +> **Using the wrong template?** +> — Bug fix: use [fix.md](?template=fix.md) +> — Enhancement to existing behavior: use [enhancement.md](?template=enhancement.md) + +--- + +## Linked Issue + +> **Required.** This PR will be auto-closed if no valid issue link is found. +> The linked issue **must** have the `approved-feature` label. If it does not, this PR will be closed without review — no exceptions. + +Closes # + +> ⛔ **No `approved-feature` label on the issue = immediate close.** +> Do not open this PR if a maintainer has not yet approved the feature spec. +> Do not open this PR if you wrote code before the issue was approved. + +--- + +## Feature summary + + + +## What changed + +### New files + + + +| File | Purpose | +|------|---------| +| | | + +### Modified files + + + +| File | What changed | +|------|-------------| +| | | + +## Implementation notes + + + +## Spec compliance + + + +- [ ] +- [ ] +- [ ] + +## Testing + +### Test coverage + + + +### Platforms tested + +- [ ] macOS +- [ ] Windows (including backslash path handling) +- [ ] Linux + +### Runtimes tested + +- [ ] Claude Code +- [ ] Gemini CLI +- [ ] OpenCode +- [ ] Codex +- [ ] Copilot +- [ ] Other: ___ +- [ ] N/A — specify which runtimes are supported and why others are excluded + +--- + +## Scope confirmation + +- [ ] The implementation matches the scope approved in the linked issue exactly +- [ ] No additional features, commands, or behaviors were added beyond what was approved +- [ ] If scope changed during implementation, I updated the issue spec and received re-approval + +--- + +## Checklist + +- [ ] Issue linked above with `Closes #NNN` — **PR will be auto-closed if missing** +- [ ] Linked issue has the `approved-feature` label — **PR will be closed if missing** +- [ ] All acceptance criteria from the issue are met (listed above) +- [ ] Implementation scope matches the approved spec exactly +- [ ] All existing tests pass (`npm test`) +- [ ] New tests cover the happy path, error cases, and edge cases +- [ ] CHANGELOG.md updated with a user-facing description of the feature +- [ ] Documentation updated — commands, workflows, references, README if applicable +- [ ] No unnecessary external dependencies added +- [ ] Works on Windows (backslash paths handled) + +## Breaking changes + + + +None + +## Screenshots / recordings + + diff --git a/.github/PULL_REQUEST_TEMPLATE/fix.md b/.github/PULL_REQUEST_TEMPLATE/fix.md new file mode 100644 index 00000000..439ae329 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/fix.md @@ -0,0 +1,74 @@ +## Fix PR + +> **Using the wrong template?** +> — Enhancement: use [enhancement.md](?template=enhancement.md) +> — Feature: use [feature.md](?template=feature.md) + +--- + +## Linked Issue + +> **Required.** This PR will be auto-closed if no valid issue link is found. + +Fixes # + +> The linked issue must have the `confirmed-bug` label. If it doesn't, ask a maintainer to confirm the bug before continuing. + +--- + +## What was broken + + + +## What this fix does + + + +## Root cause + + + +## Testing + +### How I verified the fix + + + +### Regression test added? + +- [ ] Yes — added a test that would have caught this bug +- [ ] No — explain why: + +### Platforms tested + +- [ ] macOS +- [ ] Windows (including backslash path handling) +- [ ] Linux +- [ ] N/A (not platform-specific) + +### Runtimes tested + +- [ ] Claude Code +- [ ] Gemini CLI +- [ ] OpenCode +- [ ] Other: ___ +- [ ] N/A (not runtime-specific) + +--- + +## Checklist + +- [ ] Issue linked above with `Fixes #NNN` — **PR will be auto-closed if missing** +- [ ] Linked issue has the `confirmed-bug` label +- [ ] Fix is scoped to the reported bug — no unrelated changes included +- [ ] Regression test added (or explained why not) +- [ ] All existing tests pass (`npm test`) +- [ ] CHANGELOG.md updated if this is a user-facing fix +- [ ] No unnecessary dependencies added + +## Breaking changes + + + +None diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 73735594..bafef675 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,59 +1,38 @@ -## Linked Issue +## ⚠️ Wrong template — please use the correct one for your PR type -> **Required.** PRs without a linked issue are closed without review. -> Open an issue first if one doesn't exist: https://github.com/gsd-build/get-shit-done/issues/new/choose +Every PR must use a typed template. Using this default template is a reason for rejection. -Closes # +Select the template that matches your PR: -## What +| PR Type | When to use | Template link | +|---------|-------------|---------------| +| **Fix** | Correcting a bug, crash, or behavior that doesn't match documentation | [Use fix template](?template=PULL_REQUEST_TEMPLATE/fix.md) | +| **Enhancement** | Improving an existing feature — better output, expanded edge cases, performance | [Use enhancement template](?template=PULL_REQUEST_TEMPLATE/enhancement.md) | +| **Feature** | Adding something new — new command, workflow, concept, or integration | [Use feature template](?template=PULL_REQUEST_TEMPLATE/feature.md) | - +--- -## Why +### Not sure which type applies? - +- If it **corrects broken behavior** → Fix +- If it **improves existing behavior** without adding new commands or concepts → Enhancement +- If it **adds something that doesn't exist today** → Feature +- If you are not sure → open a [Discussion](https://github.com/gsd-build/get-shit-done/discussions) first -## How +--- - +### Reminder: Issues must be approved before PRs -## Testing +For **enhancements**: the linked issue must have the `approved-enhancement` label before you open this PR. -### Platforms tested +For **features**: the linked issue must have the `approved-feature` label before you open this PR. -- [ ] macOS -- [ ] Windows (including backslash path handling) -- [ ] Linux +PRs that arrive without a labeled, approved issue are closed without review. -### Runtimes tested +See [CONTRIBUTING.md](../CONTRIBUTING.md) for the full process. -- [ ] Claude Code -- [ ] Gemini CLI -- [ ] OpenCode -- [ ] Codex -- [ ] Copilot -- [ ] N/A (not runtime-specific) +--- -### Test details - - - -## Checklist - -- [ ] Issue linked above (`Closes #NNN`) — **PR will be auto-closed if missing** -- [ ] Follows GSD style (no enterprise patterns, no filler) -- [ ] Updates CHANGELOG.md for user-facing changes -- [ ] No unnecessary dependencies added -- [ ] Works on Windows (backslash paths tested) -- [ ] Templates/references updated if behavior changed -- [ ] Existing tests pass (`npm test`) - -## Breaking Changes - - - -None - -## Screenshots / recordings - - + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c1a8a632..32a6b422 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,15 +14,81 @@ npm install npm test ``` +--- + +## Types of Contributions + +GSD accepts three types of contributions. Each type has a different process and a different bar for acceptance. **Read this section before opening anything.** + +### 🐛 Fix (Bug Report) + +A fix corrects something that is broken, crashes, produces wrong output, or behaves contrary to documented behavior. + +**Process:** +1. Open a [Bug Report issue](https://github.com/gsd-build/get-shit-done/issues/new?template=bug_report.yml) — fill it out completely. +2. Wait for a maintainer to confirm it is a bug (label: `confirmed-bug`). For obvious, reproducible bugs this is typically fast. +3. Fix it. Write a test that would have caught the bug. +4. Open a PR using the [Fix PR template](.github/PULL_REQUEST_TEMPLATE/fix.md) — link the confirmed issue. + +**Rejection reasons:** Not reproducible, works-as-designed, duplicate of an existing issue. + +--- + +### ⚡ Enhancement + +An enhancement improves an existing feature — better output, faster execution, cleaner UX, expanded edge-case handling. It does **not** add new commands, new workflows, or new concepts. + +**The bar:** Enhancements must have a scoped written proposal approved by a maintainer before any code is written. A PR for an enhancement will be closed without review if the linked issue does not carry the `approved-enhancement` label. + +**Process:** +1. Open an [Enhancement issue](https://github.com/gsd-build/get-shit-done/issues/new?template=enhancement.yml) with the full proposal. The issue template requires: the problem being solved, the concrete benefit, the scope of changes, and alternatives considered. +2. **Wait for maintainer approval.** A maintainer must label the issue `approved-enhancement` before you write a single line of code. Do not open a PR against an unapproved enhancement issue — it will be closed. +3. Write the code. Keep the scope exactly as approved. If scope creep occurs, comment on the issue and get re-approval before continuing. +4. Open a PR using the [Enhancement PR template](.github/PULL_REQUEST_TEMPLATE/enhancement.md) — link the approved issue. + +**Rejection reasons:** Issue not labeled `approved-enhancement`, scope exceeds what was approved, no written proposal, duplicate of existing behavior. + +--- + +### ✨ Feature + +A feature adds something new — a new command, a new workflow, a new concept, a new integration. Features have the highest bar because they add permanent maintenance burden to a solo-developer tool maintained by a small team. + +**The bar:** Features require a complete written specification approved by a maintainer before any code is written. A PR for a feature will be closed without review if the linked issue does not carry the `approved-feature` label. Incomplete specs are closed, not revised by maintainers. + +**Process:** +1. **Discuss first** — check [Discussions](https://github.com/gsd-build/get-shit-done/discussions) to see if the idea has been raised. If it has and was declined, don't open a new issue. +2. Open a [Feature Request issue](https://github.com/gsd-build/get-shit-done/issues/new?template=feature_request.yml) with the complete spec. The template requires: the solo-developer problem being solved, what is being added, full scope of affected files and systems, user stories, acceptance criteria, and assessment of maintenance burden. +3. **Wait for maintainer approval.** A maintainer must label the issue `approved-feature` before you write a single line of code. Approval is not guaranteed — GSD is intentionally lean and many valid ideas are declined because they conflict with the project's design philosophy. +4. Write the code. Implement exactly the approved spec. Changes to scope require re-approval. +5. Open a PR using the [Feature PR template](.github/PULL_REQUEST_TEMPLATE/feature.md) — link the approved issue. + +**Rejection reasons:** Issue not labeled `approved-feature`, spec is incomplete, scope exceeds what was approved, feature conflicts with GSD's solo-developer focus, maintenance burden too high. + +--- + +## The Issue-First Rule — No Exceptions + +> **No code before approval.** + +For **fixes**: open the issue, confirm it's a bug, then fix it. +For **enhancements**: open the issue, get `approved-enhancement`, then code. +For **features**: open the issue, get `approved-feature`, then code. + +PRs that arrive without a properly-labeled linked issue are closed automatically. This is not a bureaucratic hurdle — it protects you from spending time on work that will be rejected, and it protects maintainers from reviewing code for changes that were never agreed to. + +--- + ## Pull Request Guidelines -**Every PR must link to an open issue.** PRs without a linked issue are closed without review, no exceptions. If no issue exists for your change, open one first. +**Every PR must link to an approved issue.** PRs without a linked issue are closed without review, no exceptions. -- **Open an issue first** — describe the bug or feature before writing code. This lets maintainers confirm the direction before you invest time. +- **Use the correct PR template** — there are separate templates for [Fix](.github/PULL_REQUEST_TEMPLATE/fix.md), [Enhancement](.github/PULL_REQUEST_TEMPLATE/enhancement.md), and [Feature](.github/PULL_REQUEST_TEMPLATE/feature.md). Using the wrong template or using the default template for a feature is a rejection reason. - **Link with a closing keyword** — use `Closes #123`, `Fixes #123`, or `Resolves #123` in the PR body. The CI check will fail and the PR will be auto-closed if no valid issue reference is found. -- **One concern per PR** — bug fixes, features, and refactors should be separate PRs +- **One concern per PR** — bug fixes, enhancements, and features must be separate PRs - **No drive-by formatting** — don't reformat code unrelated to your change - **CI must pass** — all matrix jobs (Ubuntu, macOS, Windows × Node 22, 24) must be green +- **Scope matches the approved issue** — if your PR does more than what the issue describes, the extra changes will be asked to be removed or moved to a new issue ## Testing Standards @@ -35,12 +101,14 @@ const { describe, it, test, beforeEach, afterEach, before, after } = require('no const assert = require('node:assert/strict'); ``` -### Setup and Cleanup: Use Hooks, Not try/finally +### Setup and Cleanup -**Always use `beforeEach`/`afterEach` for setup and cleanup.** Do not use `try/finally` blocks for test cleanup — they are verbose, error-prone, and can mask test failures. +There are two approved cleanup patterns. Choose the one that fits the situation. + +**Pattern 1 — Shared fixtures (`beforeEach`/`afterEach`):** Use when all tests in a `describe` block share identical setup and teardown. This is the most common case. ```javascript -// GOOD — hooks handle setup/cleanup +// GOOD — shared setup/teardown with hooks describe('my feature', () => { let tmpDir; @@ -53,25 +121,39 @@ describe('my feature', () => { }); test('does the thing', () => { - // test body focuses only on the assertion assert.strictEqual(result, expected); }); }); ``` +**Pattern 2 — Per-test cleanup (`t.after()`):** Use when individual tests require unique teardown that differs from other tests in the same block. + ```javascript -// BAD — try/finally is verbose and masks failures +// GOOD — per-test cleanup when each test needs different teardown +test('does the thing with a custom setup', (t) => { + const tmpDir = createTempProject('custom-prefix'); + t.after(() => cleanup(tmpDir)); + + assert.strictEqual(result, expected); +}); +``` + +**Never use `try/finally` inside test bodies.** It is verbose, masks test failures, and is not an approved pattern in this project. + +```javascript +// BAD — try/finally inside a test body test('does the thing', () => { const tmpDir = createTempProject(); try { - // test body assert.strictEqual(result, expected); } finally { - cleanup(tmpDir); + cleanup(tmpDir); // masks failures — don't do this } }); ``` +> `try/finally` is only permitted inside standalone utility or helper functions that have no access to test context. + ### Use Centralized Test Helpers Import helpers from `tests/helpers.cjs` instead of inlining temp directory creation: @@ -126,13 +208,37 @@ describe('featureName', () => { }); ``` +### Fixture Data Formatting + +Template literals inside test blocks inherit indentation from the surrounding code. This can introduce unexpected leading whitespace that breaks regex anchors and string matching. Construct multi-line fixture strings using array `join()` instead: + +```javascript +// GOOD — no indentation bleed +const content = [ + 'line one', + 'line two', + 'line three', +].join('\n'); + +// BAD — template literal inherits surrounding indentation +const content = ` + line one + line two + line three +`; +``` + ### Node.js Version Compatibility -Tests must pass on: -- **Node 22** (LTS) -- **Node 24** (Current) +**Node 24 is the primary CI target.** All tests must pass on Node 24. Node 22 (LTS) must remain backward-compatible — do not use APIs that are not available in Node 22. -Forward-compatible with Node 26. Do not use: +| Version | Status | +|---------|--------| +| **Node 24** | Primary CI target — all tests must pass | +| **Node 22** | Backward compatibility required | +| Node 26 | Forward-compatible target — avoid deprecated APIs | + +Do not use: - Deprecated APIs - Version-specific features not available in Node 22 @@ -140,6 +246,7 @@ Safe to use: - `node:test` — stable since Node 18, fully featured in 22+ - `describe`/`it`/`test` — all supported - `beforeEach`/`afterEach`/`before`/`after` — all supported +- `t.after()` — per-test cleanup, available in Node 22+ - `t.plan()` — available since Node 22.2 - Snapshot testing — available since Node 22.3 @@ -170,6 +277,29 @@ node --test tests/core.test.cjs npm run test:coverage ``` +### Test Requirements by Contribution Type + +The required tests differ depending on what you are contributing: + +**Bug Fix:** A regression test is required. Write the test first — it must demonstrate the original failure before your fix is applied, then pass after the fix. A PR that fixes a bug without a regression test will be asked to add one. "Tests pass" does not prove correctness; it proves the bug isn't present in the tests that exist. + +**Enhancement:** Tests covering the enhanced behavior are required. Update any existing tests that test the area you changed. Do not leave tests that pass but no longer accurately describe the behavior. + +**Feature:** Tests are required for the primary success path and at minimum one failure scenario. Leaving gaps in test coverage for a new feature is a rejection reason. + +**Behavior Change:** If your change modifies existing behavior, the existing tests covering that behavior must be updated or replaced. Leaving passing-but-incorrect tests in the suite is not acceptable — a test that passes but asserts the old (now wrong) behavior makes the suite less useful than no test at all. + +### Reviewer Standards + +Reviewers do not rely solely on CI to verify correctness. Before approving a PR, reviewers: + +- Build locally (`npm run build` if applicable) +- Run the full test suite locally (`npm test`) +- Confirm regression tests exist for bug fixes and that they would fail without the fix +- Validate that the implementation matches what the linked issue described — green CI on the wrong implementation is not an approval signal + +**"Tests pass in CI" is not sufficient for merge.** The implementation must correctly solve the problem described in the linked issue. + ## Code Style - **CommonJS** (`.cjs`) — the project uses `require()`, not ESM `import`