@noice-tech/pi-changelog 1.0.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -32,7 +32,31 @@ Pi packages run with the permissions granted to Pi. **Only use this package when
32
32
 
33
33
  `auto` is accepted only as `/commit` inference input. Changelog and PR classifications are `feat`, `fix`, `improve`, or `internal`.
34
34
 
35
- The canonical shared classification rules live in `extensions/changelog/rules.md`.
35
+ `/commit` runs its worker on a side branch of the current session at low thinking, then restores your previous thinking level when it finishes. If you invoke it during an active agent turn, the change-type selector opens immediately; after your selection, the command waits for that turn to settle before starting the worker.
36
+
37
+ ### PR title package scopes
38
+
39
+ Commit messages always keep the unscoped form:
40
+
41
+ ```text
42
+ internal: update PR title generation
43
+ ```
44
+
45
+ PR titles stay unscoped in single-package repositories and workspaces. In a multi-package workspace, the title identifies one primary package by its workspace directory basename:
46
+
47
+ ```text
48
+ internal(changelog): update PR title generation
49
+ ```
50
+
51
+ The worker determines the primary package from the PR's intent and full branch against its base. Incidental shared files such as a lockfile do not override a clear primary package. Root-only, cross-cutting, or ambiguous multi-package changes use `monorepo`:
52
+
53
+ ```text
54
+ internal(monorepo): centralize release tooling
55
+ ```
56
+
57
+ Both scoped and unscoped PR titles are valid changelog inputs. Package scopes are metadata and are not included in public changelog copy.
58
+
59
+ The canonical shared classification and package-scope rules live in `extensions/changelog/rules.md`.
36
60
 
37
61
  ## Repository-specific release-note style
38
62
 
@@ -30,7 +30,7 @@ const CHANGE_TYPE_OPTIONS: Array<{ type: ChangeType; label: string }> = [
30
30
 
31
31
  const MESSAGE_TYPE = 'noice-changelog-commit-result'
32
32
  const PROMPT_MESSAGE_TYPE = 'noice-changelog-commit-worker-prompt'
33
- const STATUS_KEY = 'noice-changelog'
33
+ const COMMIT_WORKER_WIDGET_KEY = 'noice-changelog-commit-worker'
34
34
 
35
35
  type CommitDisplayStatus = 'ok' | 'cancelled' | 'failed'
36
36
 
@@ -41,6 +41,7 @@ interface CommitResultDetails {
41
41
  status?: CommitDisplayStatus
42
42
  }
43
43
 
44
+ let commitCommandPending = false
44
45
  let commitWorkerRunning = false
45
46
  let agentEndWaiter: ((messages: unknown[]) => void) | undefined
46
47
  let latestCommitWorkerMessages: unknown[] | undefined
@@ -154,26 +155,40 @@ export default function noiceChangelogExtension(pi: ExtensionAPI) {
154
155
  'Commit changes and create/update PR. Usage: /commit <changeType> <what was done>',
155
156
  getArgumentCompletions: getCommitArgumentCompletions,
156
157
  handler: async (args, ctx) => {
157
- if (commitWorkerRunning) {
158
- ctx.ui.notify('Commit worker is already running', 'warning')
158
+ if (commitCommandPending || commitWorkerRunning) {
159
+ ctx.ui.notify('Commit command is already active', 'warning')
159
160
  return
160
161
  }
161
162
 
162
- await ctx.waitForIdle()
163
+ commitCommandPending = true
164
+ let prepared: Awaited<ReturnType<typeof prepareCommit>>
165
+ try {
166
+ prepared = await prepareCommit(args, ctx)
167
+ } catch (error) {
168
+ commitCommandPending = false
169
+ throw error
170
+ }
163
171
 
164
- const parsed = await resolveChangeTypeAndContext(args, ctx)
165
- if (!parsed) {
172
+ if (!prepared) {
173
+ commitCommandPending = false
166
174
  return
167
175
  }
168
176
 
177
+ const { parsed, prompt } = prepared
169
178
  const startLeafId = ctx.sessionManager.getLeafId()
170
- const prompt = await buildWorkerPrompt(parsed.changeType, parsed.context)
179
+ const previousThinkingLevel = pi.getThinkingLevel()
171
180
 
181
+ // Establish the running guard before releasing the pending guard. Keeping
182
+ // this transition synchronous prevents a re-entrant command from starting
183
+ // a second worker and overwriting the singleton agent-end waiter.
172
184
  commitWorkerRunning = true
173
- ctx.ui.setStatus(STATUS_KEY, 'running in session branch...')
174
- ctx.ui.notify(`Starting commit worker (${parsed.changeType})`, 'info')
185
+ commitCommandPending = false
175
186
 
176
187
  try {
188
+ showCommitWorkerBanner(ctx)
189
+ ctx.ui.notify(`Starting commit worker (${parsed.changeType})`, 'info')
190
+ pi.setThinkingLevel('low')
191
+
177
192
  const agentEnd = waitForNextAgentEndAfterIdle(ctx)
178
193
  latestCommitWorkerMessages = undefined
179
194
  pi.sendMessage(
@@ -302,7 +317,8 @@ export default function noiceChangelogExtension(pi: ExtensionAPI) {
302
317
  })
303
318
  ctx.ui.notify(`Commit worker failed:\n${message}`, 'error')
304
319
  } finally {
305
- ctx.ui.setStatus(STATUS_KEY, undefined)
320
+ pi.setThinkingLevel(previousThinkingLevel)
321
+ ctx.ui.setWidget(COMMIT_WORKER_WIDGET_KEY, undefined)
306
322
  commitWorkerRunning = false
307
323
  latestCommitWorkerMessages = undefined
308
324
  }
@@ -310,6 +326,20 @@ export default function noiceChangelogExtension(pi: ExtensionAPI) {
310
326
  })
311
327
  }
312
328
 
329
+ function showCommitWorkerBanner(ctx: ExtensionCommandContext) {
330
+ const message = 'Commit worker running on a side branch of this session…'
331
+
332
+ if (ctx.mode !== 'tui') {
333
+ ctx.ui.setWidget(COMMIT_WORKER_WIDGET_KEY, [message])
334
+ return
335
+ }
336
+
337
+ ctx.ui.setWidget(
338
+ COMMIT_WORKER_WIDGET_KEY,
339
+ (_tui, theme) => new Text(theme.fg('warning', message), 1, 0)
340
+ )
341
+ }
342
+
313
343
  function getCommitArgumentCompletions(prefix: string) {
314
344
  const trimmedStart = prefix.trimStart()
315
345
  const leadingWhitespace = prefix.slice(0, prefix.length - trimmedStart.length)
@@ -341,6 +371,29 @@ function getCommitArgumentCompletions(prefix: string) {
341
371
  ]
342
372
  }
343
373
 
374
+ async function prepareCommit(
375
+ args: string | undefined,
376
+ ctx: ExtensionCommandContext
377
+ ) {
378
+ const parsed = await resolveChangeTypeAndContext(args, ctx)
379
+ if (!parsed) return null
380
+
381
+ if (!ctx.isIdle()) {
382
+ ctx.ui.notify(
383
+ 'Commit queued; waiting for the current agent turn to finish',
384
+ 'info'
385
+ )
386
+ }
387
+
388
+ await ctx.waitForIdle()
389
+ const prompt = await buildWorkerPrompt(parsed.changeType, parsed.context)
390
+ // Prompt loading is asynchronous. Re-check idle so another user turn cannot
391
+ // slip in between the original wait and worker startup.
392
+ await ctx.waitForIdle()
393
+
394
+ return { parsed, prompt }
395
+ }
396
+
344
397
  async function resolveChangeTypeAndContext(
345
398
  args: string | undefined,
346
399
  ctx: ExtensionCommandContext
@@ -6,42 +6,74 @@ Changelogs should show that products are moving without turning every release in
6
6
 
7
7
  GitHub Releases are internal shipping records. Public changelog/social text is derived from PR changelog summaries and must stand alone because repositories may be private.
8
8
 
9
- ## PR title prefixes
9
+ ## PR title format
10
10
 
11
- Every PR title should start with exactly one prefix:
11
+ Every PR title should start with exactly one change type. The title format depends on the repository's package layout:
12
12
 
13
- - `feat:` new user-facing capability
14
- - `fix:` user-facing bug fix
15
- - `improve:` user-facing refinement, UX, performance, or reliability improvement
16
- - `internal:` infra, CI, tooling, refactor, tests, dependencies, logging, or other non-user-facing work
13
+ - Single-package repository or workspace: `type: description`
14
+ - Multi-package workspace: `type(package): description`
17
15
 
18
- These prefixes are release intent, not conventional commits.
16
+ The supported change types are:
19
17
 
20
- ## Prefix rules
18
+ - `feat` — new user-facing capability
19
+ - `fix` — user-facing bug fix
20
+ - `improve` — user-facing refinement, UX, performance, or reliability improvement
21
+ - `internal` — infra, CI, tooling, refactor, tests, dependencies, logging, or other non-user-facing work
21
22
 
22
- - `feat:` means users can do something new.
23
- - `fix:` means a user-visible bug or broken behavior was corrected.
24
- - `improve:` means an existing user-facing workflow became clearer, faster, smoother, more reliable, or easier to use.
25
- - `internal:` means the work may matter to development/release/reliability but is not a public product change.
23
+ These types express release intent. They are not Conventional Commits.
26
24
 
27
- Do not use `fix:` for technical-only fixes. Use `internal:` for TypeScript fixes, build fixes, CI fixes, test fixes, dependency fixes, refactor corrections, and internal error handling unless the corrected behavior is directly user-visible.
25
+ ## Package scope rules
28
26
 
29
- ## PR titles vs public summaries
27
+ - First inspect the repository's workspace configuration and package roots. Count distinct package roots declared by the workspace configuration or its ecosystem equivalent. If there is no formal workspace configuration, count independently built or published package roots identified by the repository's manifests.
28
+ - A private root manifest used only to coordinate workspace tooling does not count as a package. A root that is itself an independently built or published package does count.
29
+ - If the repository contains zero or one package root, omit the scope. If it contains two or more, treat it as a multi-package workspace.
30
+ - In a multi-package workspace, use the primary package's workspace directory basename as the scope. For example, work primarily in `packages/renderer` uses `renderer` even if its manifest name is `@example/pi-renderer`.
31
+ - Determine the primary package from the PR's intent and the full branch diff against the detected PR base. Resolve the title from the resulting branch after the current change is committed, not from only the previous branch state, current worktree, or latest commit.
32
+ - Incidental shared-file changes, such as a root lockfile updated alongside one package, do not override a clear primary package.
33
+ - When multi-package work is root-only, cross-cutting, or has no clear primary package, use `monorepo`.
34
+ - Use exactly one scope. Do not list several package names.
30
35
 
31
- PR titles are classification and review metadata. They are not the public changelog source.
36
+ Examples:
32
37
 
33
- Good PR titles are concrete:
38
+ - Single-package repository: `feat: add branded end cards for free exports`
39
+ - Primary package: `fix(renderer): prevent hidden tracks from rendering`
40
+ - Cross-cutting multi-package work: `internal(monorepo): centralize release deployment workflow`
41
+
42
+ ## Commit message format
43
+
44
+ Commit messages always remain unscoped, regardless of repository layout:
34
45
 
35
46
  - `feat: add branded end cards for free exports`
36
47
  - `fix: prevent hidden tracks from rendering`
37
- - `improve: speed up waveform rendering for long projects`
38
48
  - `internal: centralize release deployment workflow`
39
49
 
50
+ Do not add package scopes to commit messages and do not rewrite existing commits merely to match a PR title.
51
+
52
+ ## Change type rules
53
+
54
+ - `feat` means users can do something new.
55
+ - `fix` means a user-visible bug or broken behavior was corrected.
56
+ - `improve` means an existing user-facing workflow became clearer, faster, smoother, more reliable, or easier to use.
57
+ - `internal` means the work may matter to development/release/reliability but is not a public product change.
58
+
59
+ Do not use `fix` for technical-only fixes. Use `internal` for TypeScript fixes, build fixes, CI fixes, test fixes, dependency fixes, refactor corrections, and internal error handling unless the corrected behavior is directly user-visible.
60
+
61
+ ## PR titles vs public summaries
62
+
63
+ PR titles are classification, package, and review metadata. They are not the public changelog source.
64
+
65
+ Good PR titles are concrete:
66
+
67
+ - `feat: add branded end cards for free exports`
68
+ - `fix(renderer): prevent hidden tracks from rendering`
69
+ - `improve(editor): speed up waveform rendering for long projects`
70
+ - `internal(monorepo): centralize release deployment workflow`
71
+
40
72
  Avoid vague value-prop titles:
41
73
 
42
74
  - `feat: help users create better results`
43
- - `improve: improve editor experience`
44
- - `fix: make rendering better`
75
+ - `improve(editor): improve editor experience`
76
+ - `fix(renderer): make rendering better`
45
77
 
46
78
  ## PR changelog section
47
79
 
@@ -59,9 +91,9 @@ Context:
59
91
  - ...
60
92
  ```
61
93
 
62
- For `feat:`, `fix:`, and `improve:`, `Public summary` should contain one specific standalone user-facing sentence. Write it as if it may become one bullet in a public release post.
94
+ For `feat`, `fix`, and `improve`, `Public summary` should contain one specific standalone user-facing sentence. Write it as if it may become one bullet in a public release post.
63
95
 
64
- For `internal:`, write exactly:
96
+ For `internal`, write exactly:
65
97
 
66
98
  ```md
67
99
  Public summary:
@@ -83,6 +115,8 @@ Release changelog generation should use sources in this order:
83
115
 
84
116
  If `Public summary` is `None`, skip that PR for public changelog output unless there is clear user-visible impact elsewhere in the release notes.
85
117
 
118
+ Package scopes in PR titles are metadata only. Do not copy them into public changelog text; use the canonical public summary instead.
119
+
86
120
  ## Public changelog output rules
87
121
 
88
122
  Public changelog/social text:
@@ -1,6 +1,6 @@
1
1
  You are the changelog commit worker.
2
2
 
3
- You are running in a temporary branch of the user's active Pi session. Use the provided change type and short user description as the primary source for commit/PR wording. Use git diff only to verify that the description matches the actual changes and to catch important omissions; do not try to rediscover or guess the change from the diff when a description is provided.
3
+ You are running in a temporary branch of the user's active Pi session. Use the provided change type and short user description as the primary source for the current commit wording and for PR changelog text about the current change. Use the current diff only to verify that description and catch important omissions; do not try to rediscover or guess the current change from the diff when a description is provided. Resolve the PR title separately from the cumulative full branch as described below.
4
4
 
5
5
  Task:
6
6
  Commit current changes and create or update the GitHub PR.
@@ -20,29 +20,35 @@ Before choosing commit messages, PR title, or PR changelog text, read and follow
20
20
 
21
21
  Workflow:
22
22
 
23
- 1. Inspect git status, current branch, diff, branch commits, candidate base branch, and existing PR.
23
+ 1. Inspect git status, current branch, staged and unstaged changes, branch commits, candidate base branch, existing PR, and repository workspace/package layout.
24
24
  2. If a PR exists, read its current title, base branch, and full body before deciding what to change.
25
- 3. If there are no changes to commit, update the PR body if useful, otherwise report no-op.
26
- 4. If on main, create a branch.
27
- 5. Commit current changes with a good prefixed commit message.
28
- 6. Push the branch.
29
- 7. If no PR exists for the branch, create one against the detected base branch.
30
- 8. If PR exists, update title/body to reflect the full branch while preserving useful existing PR description content and existing base branch.
31
- 9. When creating a PR body, always write the final markdown body to a file in a temporary directory and pass it to GitHub CLI with `--body-file`; do not pass markdown through `--body`.
32
- 10. When updating an existing PR, avoid `gh pr edit`; it can fail on some repositories because GitHub CLI queries deprecated Projects Classic GraphQL fields. Use the REST API fallback described below instead.
25
+ 3. Determine whether there are changes to commit or useful PR metadata updates to make. If there are neither, report no-op.
26
+ 4. If there are changes to commit and the current branch is main, create a branch.
27
+ 5. If there are changes to commit, commit them with a good unscoped prefixed commit message.
28
+ 6. Resolve the PR title format and, for a multi-package workspace, its one primary package from the PR's cumulative intent and the resulting full branch diff against the detected or preserved PR base. Resolve this after committing the current changes so the diff includes them; do not use only the latest commit.
29
+ 7. Push the branch if needed.
30
+ 8. If no PR exists for the branch, create one against the detected base branch.
31
+ 9. If a PR exists, update its title/body to reflect the full branch while preserving useful existing PR description content and its existing base branch.
32
+ 10. When creating a PR body, always write the final markdown body to a file in a temporary directory and pass it to GitHub CLI with `--body-file`; do not pass markdown through `--body`.
33
+ 11. When updating an existing PR, avoid `gh pr edit`; it can fail on some repositories because GitHub CLI queries deprecated Projects Classic GraphQL fields. Use the REST API fallback described below instead.
33
34
 
34
35
  Rules:
35
36
 
36
- - Use the selected change type as user intent, unless it clearly contradicts the provided description and diff.
37
- - If selected type is `auto`, infer the type from the provided description first, then session context and diff, using the changelog rules.
38
- - Treat `whatWasDoneShort` as the user's rough wording. Convert awkward, terse, or informal language into clear PR title, commit message, and changelog wording according to the changelog rules.
39
- - Prefer the user's description over diff-derived wording. Use the diff to verify accuracy and specificity, not to invent a different story.
40
- - If the user's description is missing or too vague, use session context and diff as fallback.
41
- - `fix:` is only for user-visible bug fixes. Technical-only fixes must use `internal:`.
42
- - Commit message describes the current change using the user's description refined by the rules.
43
- - PR title describes the full branch using the user's description refined by the rules.
44
- - PR title must start with exactly one prefix from the changelog rules.
45
- - PR title is classification/review metadata, not the public changelog summary.
37
+ - Use the selected change type as user intent for the current commit, unless it clearly contradicts the provided description and diff.
38
+ - If selected type is `auto`, infer the current commit's type from the provided description first, then session context and diff, using the changelog rules.
39
+ - Treat `whatWasDoneShort` as the user's rough wording for the current change. Convert awkward, terse, or informal language into a clear commit message and changelog wording according to the changelog rules.
40
+ - Prefer the user's description over diff-derived wording for the current commit. Use the current diff to verify accuracy and specificity, not to invent a different story.
41
+ - The PR title must describe the cumulative full branch. Use the user's description for it only when that description represents the full branch; otherwise use the existing PR title/body, branch commits, session context, and full branch diff to preserve the established branch intent. Do not let the latest delta replace a broader PR purpose.
42
+ - If the user's description is missing or too vague for the current change, use session context and the current diff as fallback.
43
+ - `fix` is only for user-visible bug fixes. Technical-only fixes must use `internal`.
44
+ - Commit messages always use the unscoped `type: description` format, including in multi-package workspaces. Do not add a package scope to a commit message or rewrite existing commits to match the PR title.
45
+ - Derive the PR title from the cumulative full-branch sources described above, not by defaulting to the latest current-change description.
46
+ - In a single-package repository or workspace, the PR title uses `type: description`.
47
+ - In a multi-package workspace, the PR title uses `type(package): description`, where `package` is the primary package's workspace directory basename.
48
+ - For root-only or cross-cutting work in a multi-package workspace, or when no one package is clearly primary, use `type(monorepo): description`.
49
+ - Incidental shared files such as a root lockfile do not override a clear primary package. Use exactly one package scope rather than listing every affected package.
50
+ - When updating an existing PR, preserve its package scope only if it still matches the repository layout and full branch; otherwise correct the title.
51
+ - PR title type and package scope are classification/review metadata, not the public changelog summary.
46
52
  - PR body `## Changelog` → `Public summary` is the canonical source for future public changelog generation.
47
53
  - Do not use vague value-prop titles.
48
54
  - Do not modify source files unless absolutely required to complete commit/PR metadata.
@@ -76,7 +82,7 @@ PR body handling:
76
82
  ```sh
77
83
  repo=$(gh repo view --json nameWithOwner --jq .nameWithOwner)
78
84
  pr_number=$(gh pr view --json number --jq .number)
79
- title='improve: concise PR title here'
85
+ title="$resolved_pr_title"
80
86
  title_json=$(jq -Rn --arg value "$title" '$value')
81
87
  body_json=$(jq -Rs . < "$body_file")
82
88
  gh api "repos/$repo/pulls/$pr_number" \
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noice-tech/pi-changelog",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "description": "Pi changelog workflow for commits, pull requests, and public release notes.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -48,6 +48,6 @@
48
48
  },
49
49
  "scripts": {
50
50
  "typecheck": "tsc --noEmit --allowImportingTsExtensions --moduleResolution bundler --module ESNext --target ES2022 --skipLibCheck extensions/changelog/index.ts",
51
- "test": "node ../../scripts/smoke-pack.mjs @noice-tech/pi-changelog packages/changelog"
51
+ "test": "node --test test/*.test.mjs && node ../../scripts/smoke-pack.mjs @noice-tech/pi-changelog packages/changelog"
52
52
  }
53
53
  }
@@ -25,13 +25,14 @@ If `.pi/release-notes-style.md` exists, read it before writing public copy. It i
25
25
 
26
26
  ## Shared changelog rules
27
27
 
28
- PR title prefixes are `feat:`, `fix:`, `improve:`, and `internal:`.
29
-
30
- - `feat:` means users can do something new.
31
- - `fix:` means a user-visible bug or broken behavior was corrected.
32
- - `improve:` means an existing user-facing workflow became clearer, faster, smoother, more reliable, or easier to use.
33
- - `internal:` means the work may matter to development, release, or reliability but is not a public product change.
34
- - Technical-only TypeScript, build, CI, test, dependency, refactor, and internal error-handling fixes are `internal:` unless corrected behavior is directly user-visible.
28
+ Valid PR titles use either unscoped `type: description` or package-scoped `type(package): description` form. The change types are `feat`, `fix`, `improve`, and `internal`.
29
+
30
+ - `feat` means users can do something new.
31
+ - `fix` means a user-visible bug or broken behavior was corrected.
32
+ - `improve` means an existing user-facing workflow became clearer, faster, smoother, more reliable, or easier to use.
33
+ - `internal` means the work may matter to development, release, or reliability but is not a public product change.
34
+ - Technical-only TypeScript, build, CI, test, dependency, refactor, and internal error-handling fixes are `internal` unless corrected behavior is directly user-visible.
35
+ - A package scope, including `monorepo`, is metadata. It does not change the change type and must not be copied into public changelog text.
35
36
  - PR titles classify work; they are not the public changelog source.
36
37
  - PR body `## Changelog` → `Public summary` is the canonical public changelog atom.
37
38
 
@@ -45,7 +46,7 @@ Use sources in this order:
45
46
  4. PR title, only for classification or fallback
46
47
  5. Commit messages, only as a last fallback
47
48
 
48
- Include meaningful `feat:`, `fix:`, and `improve:` changes whose public summary is not `None`. Skip `internal:` changes and summaries of `None`. Be conservative with fixes.
49
+ Include meaningful `feat`, `fix`, and `improve` changes, whether scoped or unscoped, whose public summary is not `None`. Skip `internal` changes and summaries of `None`. Be conservative with fixes.
49
50
 
50
51
  ## Public copy
51
52
 
@@ -12,12 +12,14 @@ Ignore arguments if provided. This command previews the current unreleased state
12
12
 
13
13
  Follow these shared changelog rules exactly:
14
14
 
15
- - PR title prefixes: `feat:`, `fix:`, `improve:`, and `internal:`.
16
- - `feat:` means users can do something new.
17
- - `fix:` means a user-visible bug or broken behavior was corrected.
18
- - `improve:` means an existing user-facing workflow became clearer, faster, smoother, more reliable, or easier to use.
19
- - `internal:` means the work may matter to development/release/reliability but is not a public product change.
20
- - Do not use technical-only fixes as public fixes. TypeScript, build, CI, test, dependency, refactor, and internal error-handling fixes are `internal:` unless the corrected behavior is directly user-visible.
15
+ - Valid PR titles use either unscoped `type: description` or package-scoped `type(package): description` form.
16
+ - The change types are `feat`, `fix`, `improve`, and `internal`.
17
+ - `feat` means users can do something new.
18
+ - `fix` means a user-visible bug or broken behavior was corrected.
19
+ - `improve` means an existing user-facing workflow became clearer, faster, smoother, more reliable, or easier to use.
20
+ - `internal` means the work may matter to development/release/reliability but is not a public product change.
21
+ - Do not use technical-only fixes as public fixes. TypeScript, build, CI, test, dependency, refactor, and internal error-handling fixes are `internal` unless the corrected behavior is directly user-visible.
22
+ - A package scope, including `monorepo`, is metadata. It does not change the change type and must not be copied into public changelog text.
21
23
  - PR titles are classification/review metadata. They are not the public changelog source.
22
24
  - PR body `## Changelog` → `Public summary` is the canonical public changelog atom.
23
25
 
@@ -41,10 +43,10 @@ Workflow:
41
43
 
42
44
  Public candidate rules:
43
45
 
44
- - Include `feat:`, `fix:`, and `improve:` PRs when `Public summary` contains a meaningful user-facing summary.
45
- - Skip `internal:` PRs by default.
46
+ - Include `feat`, `fix`, and `improve` PRs, whether scoped or unscoped, when `Public summary` contains a meaningful user-facing summary.
47
+ - Skip `internal` PRs by default, whether scoped or unscoped.
46
48
  - Skip PRs where `Public summary` is `None`.
47
- - Be conservative with `fix:`. Include only user-visible fixes, not technical/build/CI/test/refactor/dependency fixes.
49
+ - Be conservative with `fix`. Include only user-visible fixes, not technical/build/CI/test/refactor/dependency fixes.
48
50
  - Use `Public summary` text as the candidate release bullet when available.
49
51
  - If the PR appears user-facing but lacks a usable public summary, put it under `Needs cleanup`, not `Public candidates`.
50
52
 
@@ -55,8 +57,8 @@ Flag PRs that may affect release quality, especially:
55
57
  - missing `## Changelog` section
56
58
  - missing `Public summary`
57
59
  - vague public summary such as “Improved experience” or “Bug fixes”
58
- - `feat:`, `fix:`, or `improve:` PR with `Public summary: None`
59
- - title prefix appears inconsistent with the changelog body
60
+ - `feat`, `fix`, or `improve` PR, whether scoped or unscoped, with `Public summary: None`
61
+ - title change type appears inconsistent with the changelog body
60
62
  - possible user-facing change only discoverable from title/commits, not from `Public summary`
61
63
 
62
64
  Output format: