PRD numbering: adopt prd-new placeholder + post-merge workflow to eliminate merge-time conflicts #213

Closed
opened 2026-06-06 16:49:47 -04:00 by didericis-claude · 4 comments
Collaborator

Problem

PRD numbers are assigned at branch-creation time and collide when multiple branches are open simultaneously. This session required renumbering six PRDs after the fact (0051→0054, three branches all claiming 0053, etc.) because there is no mechanism to defer numbering until merge.

Proposed conventions

1. Drop PRD numbers from branch names

Branch names should not include a PRD number. Use a short descriptive slug instead:

# Before
prd-0053-extended-outbound-scan

# After
egress-extended-outbound-scan

This is the highest-value change and has zero infrastructure cost. Dropping the number from branch names alone would have prevented most of the friction this pattern causes.

2. Use prd-new as a placeholder filename until merge

PRD files on a branch are named prd-new-<slug>.md (e.g. prd-new-install-script.md). The header reads # PRD NEW: <Title> and Status: Draft. The number is not assigned until the file lands on main.

PR titles use the slug, not a number: egress-extended-outbound-scan: extend DLP scan surfaces.

3. A post-merge workflow on main assigns the number and flips Status

On every push to main that adds a prd-new-*.md file, a Gitea Actions workflow:

  1. Determines the next available PRD number by reading docs/prds/ at that commit.
  2. Renames prd-new-<slug>.mdNNNN-<slug>.md.
  3. Replaces # PRD NEW: with # PRD NNNN: in the file header.
  4. Checks whether the merged commit touches any files outside docs/prds/. If yes (implementation shipped in the same PR), flips Status: DraftStatus: Active. If no (PRD-only PR — implementation follows separately), leaves Status as Draft.
  5. Commits the rename + any header updates with message docs(prd): number prd-new-<slug> → PRD NNNN.

Numbering is serialised through main's linear history, so two PRDs merging in quick succession get different numbers based on push order — no human coordination needed. PRs continue to target main directly; no staging branch is needed.

4. (Ideal) Block direct merges to main that include a prd-new file via a required PR status check

A required Gitea Actions status check on PRs targeting main fails if the PR diff adds any prd-new-*.md file and the PR was not opened by the numbering workflow itself. This prevents the bypass path where a developer manually numbers a PRD and pushes directly.

A required status check is easier to maintain than a server-side pre-receive hook — it lives in .gitea/workflows/ alongside the rest of CI and does not require server configuration.

Scope

  • Update AGENTS.md and docs/prds/README.md with the new naming convention.
  • Add .gitea/workflows/prd-number.yml (post-merge numbering + Status workflow).
  • Add .gitea/workflows/prd-check.yml (PR status check that rejects raw prd-new files targeting main directly).

Non-goals

  • Retroactively renaming existing numbered PRDs already on main.
  • Changing the PRD format or sections.
  • A separate prd-promotion staging branch (unnecessary given post-merge numbering on main).
  • Automated PR creation or merging.
## Problem PRD numbers are assigned at branch-creation time and collide when multiple branches are open simultaneously. This session required renumbering six PRDs after the fact (0051→0054, three branches all claiming 0053, etc.) because there is no mechanism to defer numbering until merge. ## Proposed conventions ### 1. Drop PRD numbers from branch names Branch names should not include a PRD number. Use a short descriptive slug instead: ``` # Before prd-0053-extended-outbound-scan # After egress-extended-outbound-scan ``` This is the highest-value change and has zero infrastructure cost. Dropping the number from branch names alone would have prevented most of the friction this pattern causes. ### 2. Use `prd-new` as a placeholder filename until merge PRD files on a branch are named `prd-new-<slug>.md` (e.g. `prd-new-install-script.md`). The header reads `# PRD NEW: <Title>` and `Status: Draft`. The number is not assigned until the file lands on `main`. PR titles use the slug, not a number: `egress-extended-outbound-scan: extend DLP scan surfaces`. ### 3. A post-merge workflow on `main` assigns the number and flips Status On every push to `main` that adds a `prd-new-*.md` file, a Gitea Actions workflow: 1. Determines the next available PRD number by reading `docs/prds/` at that commit. 2. Renames `prd-new-<slug>.md` → `NNNN-<slug>.md`. 3. Replaces `# PRD NEW:` with `# PRD NNNN:` in the file header. 4. **Checks whether the merged commit touches any files outside `docs/prds/`.** If yes (implementation shipped in the same PR), flips `Status: Draft` → `Status: Active`. If no (PRD-only PR — implementation follows separately), leaves Status as Draft. 5. Commits the rename + any header updates with message `docs(prd): number prd-new-<slug> → PRD NNNN`. Numbering is serialised through `main`'s linear history, so two PRDs merging in quick succession get different numbers based on push order — no human coordination needed. PRs continue to target `main` directly; no staging branch is needed. ### 4. (Ideal) Block direct merges to `main` that include a `prd-new` file via a required PR status check A required Gitea Actions status check on PRs targeting `main` fails if the PR diff adds any `prd-new-*.md` file and the PR was not opened by the numbering workflow itself. This prevents the bypass path where a developer manually numbers a PRD and pushes directly. A required status check is easier to maintain than a server-side `pre-receive` hook — it lives in `.gitea/workflows/` alongside the rest of CI and does not require server configuration. ## Scope - Update `AGENTS.md` and `docs/prds/README.md` with the new naming convention. - Add `.gitea/workflows/prd-number.yml` (post-merge numbering + Status workflow). - Add `.gitea/workflows/prd-check.yml` (PR status check that rejects raw `prd-new` files targeting `main` directly). ## Non-goals - Retroactively renaming existing numbered PRDs already on `main`. - Changing the PRD format or sections. - A separate `prd-promotion` staging branch (unnecessary given post-merge numbering on `main`). - Automated PR creation or merging.
didericis-claude changed title from PRD numbering: adopt prd-new + promotion-branch workflow to eliminate merge-time conflicts to PRD numbering: adopt prd-new placeholder + post-merge workflow to eliminate merge-time conflicts 2026-06-06 21:45:32 -04:00
Owner

I think status should be auto updated as well: is a little awkward/introduces a brief period post merge when a prd thats implemented might be marked “draft”, but its already a bit awkward to keep the “new” tag. Avoids risk of forgetting to flip the status, which is high for manual prs.

I think status should be auto updated as well: is a little awkward/introduces a brief period post merge when a prd thats implemented might be marked “draft”, but its already a bit awkward to keep the “new” tag. Avoids risk of forgetting to flip the status, which is high for manual prs.
Author
Collaborator

Agreed — the "brief Draft window" argument is weaker than the "will definitely forget" argument, especially for agent-opened PRs where there's no natural moment to go back and flip the status.

The one edge case worth calling out: a PRD can land on main before its implementation does (PRD-only PR merges first, implementation follows in a separate PR). In that case auto-flipping to Active at numbering time would be wrong — the PRD would be marked Active before anything ships.

Two options:

  1. Flip on numbering, accept the edge case — simple rule, easy to automate, occasionally inaccurate for split PRD+implementation workflows. Since most PRDs here ship in the same PR as the implementation, this is usually correct.

  2. Flip on numbering only when the PR also contains non-PRD file changes — the workflow checks whether the merged commit touches anything outside docs/prds/; if yes, flip to Active; if no (PRD-only PR), leave as Draft. Slightly more logic in the workflow but closer to the semantic intent.

Option 2 seems worth the small extra complexity given that PRD-only PRs are a real pattern here. Leaning toward that — does that match your expectation?

Agreed — the "brief Draft window" argument is weaker than the "will definitely forget" argument, especially for agent-opened PRs where there's no natural moment to go back and flip the status. The one edge case worth calling out: a PRD can land on `main` before its implementation does (PRD-only PR merges first, implementation follows in a separate PR). In that case auto-flipping to Active at numbering time would be wrong — the PRD would be marked Active before anything ships. Two options: 1. **Flip on numbering, accept the edge case** — simple rule, easy to automate, occasionally inaccurate for split PRD+implementation workflows. Since most PRDs here ship in the same PR as the implementation, this is usually correct. 2. **Flip on numbering only when the PR also contains non-PRD file changes** — the workflow checks whether the merged commit touches anything outside `docs/prds/`; if yes, flip to Active; if no (PRD-only PR), leave as Draft. Slightly more logic in the workflow but closer to the semantic intent. Option 2 seems worth the small extra complexity given that PRD-only PRs are a real pattern here. Leaning toward that — does that match your expectation?
Owner

RE #213 (comment)

Option 2 is a good idea, add it

RE https://gitea.dideric.is/didericis/bot-bottle/issues/213#issuecomment-1774 Option 2 is a good idea, add it
Author
Collaborator

Done — updating the issue now to incorporate option 2 into the workflow spec.

Done — updating the issue now to incorporate option 2 into the workflow spec.
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: didericis/bot-bottle#213