@1aboveio/skills 1.0.8 → 1.0.10

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.
Files changed (214) hide show
  1. package/.codex-plugin/plugin.json +13 -0
  2. package/.codex-plugin/resources.json +1827 -0
  3. package/README.md +22 -0
  4. package/package.json +9 -2
  5. package/skills/app-dev/airflow/SKILL.md +1 -1
  6. package/skills/app-dev/airflow/release-check.mjs +3 -1
  7. package/skills/app-dev/android-foundation/SKILL.md +1 -1
  8. package/skills/app-dev/android-foundation/release-check.mjs +3 -1
  9. package/skills/app-dev/app-debug/SKILL.md +1 -1
  10. package/skills/app-dev/app-debug/release-check.mjs +3 -1
  11. package/skills/app-dev/better-auth/SKILL.md +1 -1
  12. package/skills/app-dev/better-auth/release-check.mjs +3 -1
  13. package/skills/app-dev/data-catalog/SKILL.md +1 -1
  14. package/skills/app-dev/data-catalog/release-check.mjs +3 -1
  15. package/skills/app-dev/data-quality/SKILL.md +1 -1
  16. package/skills/app-dev/data-quality/release-check.mjs +3 -1
  17. package/skills/app-dev/ios-foundation/SKILL.md +1 -1
  18. package/skills/app-dev/ios-foundation/release-check.mjs +3 -1
  19. package/skills/app-dev/monorepo/SKILL.md +1 -1
  20. package/skills/app-dev/monorepo/release-check.mjs +3 -1
  21. package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
  22. package/skills/app-dev/nextjs-fullstack/release-check.mjs +3 -1
  23. package/skills/app-dev/prisma-setup/SKILL.md +1 -1
  24. package/skills/app-dev/prisma-setup/release-check.mjs +3 -1
  25. package/skills/app-dev/pyspark/SKILL.md +1 -1
  26. package/skills/app-dev/pyspark/release-check.mjs +3 -1
  27. package/skills/app-dev/python-backend/SKILL.md +1 -1
  28. package/skills/app-dev/python-backend/release-check.mjs +3 -1
  29. package/skills/app-dev/shadcn/SKILL.md +1 -1
  30. package/skills/app-dev/shadcn/release-check.mjs +3 -1
  31. package/skills/app-dev/zod-v4/SKILL.md +1 -1
  32. package/skills/app-dev/zod-v4/release-check.mjs +3 -1
  33. package/skills/cicd/cloud-build/SKILL.md +1 -1
  34. package/skills/cicd/cloud-build/release-check.mjs +3 -1
  35. package/skills/cicd/cloud-debug/SKILL.md +1 -1
  36. package/skills/cicd/cloud-debug/release-check.mjs +3 -1
  37. package/skills/cicd/cloud-deploy/SKILL.md +1 -1
  38. package/skills/cicd/cloud-deploy/release-check.mjs +3 -1
  39. package/skills/cicd/docker/SKILL.md +1 -1
  40. package/skills/cicd/docker/release-check.mjs +3 -1
  41. package/skills/cicd/github-actions/SKILL.md +1 -1
  42. package/skills/cicd/github-actions/release-check.mjs +3 -1
  43. package/skills/cicd/github-flow/SKILL.md +1 -1
  44. package/skills/cicd/github-flow/release-check.mjs +3 -1
  45. package/skills/cicd/google-cloud/SKILL.md +1 -1
  46. package/skills/cicd/google-cloud/release-check.mjs +3 -1
  47. package/skills/cicd/mergify/SKILL.md +1 -1
  48. package/skills/cicd/mergify/release-check.mjs +3 -1
  49. package/skills/cicd/podman/SKILL.md +1 -1
  50. package/skills/cicd/podman/release-check.mjs +3 -1
  51. package/skills/content/article-review/SKILL.md +1 -1
  52. package/skills/content/article-review/release-check.mjs +3 -1
  53. package/skills/content/article-to-image/SKILL.md +1 -1
  54. package/skills/content/article-to-image/release-check.mjs +3 -1
  55. package/skills/content/flowchart-svg/SKILL.md +1 -1
  56. package/skills/content/flowchart-svg/release-check.mjs +3 -1
  57. package/skills/content/image-gen/SKILL.md +1 -1
  58. package/skills/content/image-gen/release-check.mjs +3 -1
  59. package/skills/content/mermaid/SKILL.md +1 -1
  60. package/skills/content/mermaid/release-check.mjs +3 -1
  61. package/skills/content/wechat-article-writer/SKILL.md +1 -1
  62. package/skills/content/wechat-article-writer/release-check.mjs +3 -1
  63. package/skills/content/wechat-publisher/SKILL.md +1 -1
  64. package/skills/content/wechat-publisher/release-check.mjs +3 -1
  65. package/skills/content/wechat-search/SKILL.md +1 -1
  66. package/skills/content/wechat-search/release-check.mjs +3 -1
  67. package/skills/content/xai-search/SKILL.md +88 -13
  68. package/skills/content/xai-search/release-check.mjs +3 -1
  69. package/skills/content/xai-search/scripts/xai_search.sh +125 -149
  70. package/skills/core/distill-writing/SKILL.md +1 -1
  71. package/skills/core/distill-writing/release-check.mjs +3 -1
  72. package/skills/core/doc-to-md/SKILL.md +1 -1
  73. package/skills/core/doc-to-md/release-check.mjs +3 -1
  74. package/skills/core/editorial-review/SKILL.md +1 -1
  75. package/skills/core/editorial-review/release-check.mjs +3 -1
  76. package/skills/core/house-style/SKILL.md +1 -1
  77. package/skills/core/house-style/release-check.mjs +3 -1
  78. package/skills/core/plain-language/SKILL.md +1 -1
  79. package/skills/core/plain-language/release-check.mjs +3 -1
  80. package/skills/core/polish-cn/SKILL.md +1 -1
  81. package/skills/core/polish-cn/release-check.mjs +3 -1
  82. package/skills/core/reg-parser/SKILL.md +1 -1
  83. package/skills/core/reg-parser/release-check.mjs +3 -1
  84. package/skills/core/terminology/SKILL.md +1 -1
  85. package/skills/core/terminology/release-check.mjs +3 -1
  86. package/skills/core/write-doc/SKILL.md +1 -1
  87. package/skills/core/write-doc/release-check.mjs +3 -1
  88. package/skills/engineering/better-review/SKILL.md +1 -1
  89. package/skills/engineering/better-review/release-check.mjs +3 -1
  90. package/skills/engineering/contributing/SKILL.md +1 -1
  91. package/skills/engineering/contributing/release-check.mjs +3 -1
  92. package/skills/engineering/e2e-test/SKILL.md +1 -1
  93. package/skills/engineering/e2e-test/release-check.mjs +3 -1
  94. package/skills/engineering/engineering-runtime/SKILL.md +1 -1
  95. package/skills/engineering/engineering-runtime/coherence/workflow.json +13 -13
  96. package/skills/engineering/engineering-runtime/release-check.mjs +3 -1
  97. package/skills/engineering/engineering-runtime/scripts/installation-resolver.mjs +191 -0
  98. package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +51 -10
  99. package/skills/engineering/ensure-coverage/SKILL.md +1 -1
  100. package/skills/engineering/ensure-coverage/release-check.mjs +3 -1
  101. package/skills/engineering/golden-path/SKILL.md +1 -1
  102. package/skills/engineering/golden-path/release-check.mjs +3 -1
  103. package/skills/engineering/harness-runtime/SKILL.md +1 -1
  104. package/skills/engineering/harness-runtime/release-check.mjs +3 -1
  105. package/skills/engineering/resolve-issues/SKILL.md +1 -1
  106. package/skills/engineering/resolve-issues/references/preflight.md +7 -0
  107. package/skills/engineering/resolve-issues/references/shared-modules.md +27 -0
  108. package/skills/engineering/resolve-issues/references/task-plan.md +5 -1
  109. package/skills/engineering/resolve-issues/release-check.mjs +3 -1
  110. package/skills/engineering/resolve-issues/scripts/active-installation.mjs +32 -0
  111. package/skills/engineering/resolve-issues/scripts/install-hooks.mjs +3 -0
  112. package/skills/engineering/resolve-issues/scripts/rehydrate.mjs +17 -1
  113. package/skills/engineering/resolve-release/SKILL.md +1 -1
  114. package/skills/engineering/resolve-release/release-check.mjs +3 -1
  115. package/skills/engineering/rush-issues/SKILL.md +1 -1
  116. package/skills/engineering/rush-issues/release-check.mjs +3 -1
  117. package/skills/engineering/rush-release/SKILL.md +1 -1
  118. package/skills/engineering/rush-release/release-check.mjs +3 -1
  119. package/skills/engineering/test-quality/SKILL.md +1 -1
  120. package/skills/engineering/test-quality/release-check.mjs +3 -1
  121. package/skills/risk-compliance/compliance-audit/SKILL.md +1 -1
  122. package/skills/risk-compliance/compliance-audit/release-check.mjs +3 -1
  123. package/skills/risk-compliance/contract-compare/SKILL.md +1 -1
  124. package/skills/risk-compliance/contract-compare/release-check.mjs +3 -1
  125. package/skills/risk-compliance/contract-review/SKILL.md +1 -1
  126. package/skills/risk-compliance/contract-review/release-check.mjs +3 -1
  127. package/skills/risk-compliance/doc-authenticity/SKILL.md +1 -1
  128. package/skills/risk-compliance/doc-authenticity/release-check.mjs +3 -1
  129. package/skills/risk-compliance/fraud-analysis/SKILL.md +1 -1
  130. package/skills/risk-compliance/fraud-analysis/release-check.mjs +3 -1
  131. package/skills/risk-compliance/payment-analysis/SKILL.md +1 -1
  132. package/skills/risk-compliance/payment-analysis/release-check.mjs +3 -1
  133. package/skills/risk-compliance/similar-domain/SKILL.md +1 -1
  134. package/skills/risk-compliance/similar-domain/release-check.mjs +3 -1
  135. package/skills/risk-compliance/tld-detection/SKILL.md +1 -1
  136. package/skills/risk-compliance/tld-detection/release-check.mjs +3 -1
  137. package/skills/app-dev/android-foundation/scripts/device-run.test.mjs +0 -436
  138. package/skills/app-dev/data-catalog/scripts/data-catalog.test.mjs +0 -36
  139. package/skills/app-dev/data-quality/scripts/data-quality.test.mjs +0 -12
  140. package/skills/cicd/github-actions/scripts/ci-run-audit.test.mjs +0 -637
  141. package/skills/cicd/github-flow/scripts/flow.test.mjs +0 -114
  142. package/skills/cicd/github-flow/scripts/persist-conventions.test.mjs +0 -108
  143. package/skills/cicd/mergify/scripts/audit.test.mjs +0 -201
  144. package/skills/cicd/mergify/scripts/entrypoints.test.mjs +0 -106
  145. package/skills/cicd/mergify/scripts/pr-620-mergify-comments.fixture.json +0 -30
  146. package/skills/cicd/mergify/scripts/stack-push.test.mjs +0 -233
  147. package/skills/cicd/mergify/scripts/watch-pr-delivery.test.mjs +0 -1826
  148. package/skills/content/image-gen/scripts/credentials.test.mjs +0 -212
  149. package/skills/content/image-gen/scripts/options.test.mjs +0 -245
  150. package/skills/content/xai-search/tests/smoke_test.sh +0 -36
  151. package/skills/core/polish-cn/scripts/polish-cn.test.mjs +0 -244
  152. package/skills/core/reg-parser/scripts/reg-parser.test.mjs +0 -165
  153. package/skills/engineering/better-review/scripts/pr-context.test.mjs +0 -168
  154. package/skills/engineering/better-review/scripts/rereview-scope.test.mjs +0 -587
  155. package/skills/engineering/better-review/scripts/scan-diff.test.mjs +0 -414
  156. package/skills/engineering/better-review/scripts/vendor-sync.test.mjs +0 -96
  157. package/skills/engineering/contributing/scripts/persist-conventions.test.mjs +0 -118
  158. package/skills/engineering/e2e-test/scripts/presentation-checks.test.mjs +0 -106
  159. package/skills/engineering/e2e-test/scripts/presentation-perceivability.test.mjs +0 -117
  160. package/skills/engineering/e2e-test/scripts/presentation-reachability.test.mjs +0 -69
  161. package/skills/engineering/e2e-test/scripts/presentation-render-health.test.mjs +0 -98
  162. package/skills/engineering/e2e-test/scripts/presentation-sweep.test.mjs +0 -71
  163. package/skills/engineering/e2e-test/scripts/presentation-temporal.test.mjs +0 -59
  164. package/skills/engineering/e2e-test/scripts/project-route-manifest.test.mjs +0 -76
  165. package/skills/engineering/engineering-runtime/engineering-runtime.test.mjs +0 -68
  166. package/skills/engineering/engineering-runtime/scripts/invocation-policy.test.mjs +0 -95
  167. package/skills/engineering/engineering-runtime/scripts/main-module.test.mjs +0 -288
  168. package/skills/engineering/engineering-runtime/scripts/required-check-plan.test.mjs +0 -96
  169. package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.test.mjs +0 -96
  170. package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.test.mjs +0 -47
  171. package/skills/engineering/ensure-coverage/scripts/ci-audit.test.mjs +0 -86
  172. package/skills/engineering/ensure-coverage/scripts/coverage-ledger.test.mjs +0 -447
  173. package/skills/engineering/ensure-coverage/scripts/design-parity.test.mjs +0 -552
  174. package/skills/engineering/ensure-coverage/scripts/lint-tests.test.mjs +0 -289
  175. package/skills/engineering/ensure-coverage/scripts/mock-policy-config.test.mjs +0 -119
  176. package/skills/engineering/golden-path/scripts/golden-path.test.mjs +0 -1457
  177. package/skills/engineering/harness-runtime/discover-models.test.mjs +0 -363
  178. package/skills/engineering/harness-runtime/harness-runtime.test.mjs +0 -967
  179. package/skills/engineering/harness-runtime/model-catalog-seed.test.mjs +0 -829
  180. package/skills/engineering/harness-runtime/model-catalog.test.mjs +0 -366
  181. package/skills/engineering/resolve-issues/scripts/cleanup-runs.test.mjs +0 -280
  182. package/skills/engineering/resolve-issues/scripts/dropbox.test.mjs +0 -346
  183. package/skills/engineering/resolve-issues/scripts/findings-reuse.test.mjs +0 -88
  184. package/skills/engineering/resolve-issues/scripts/github-reviews.test.mjs +0 -264
  185. package/skills/engineering/resolve-issues/scripts/install-hooks.test.mjs +0 -79
  186. package/skills/engineering/resolve-issues/scripts/issue-tree.test.mjs +0 -107
  187. package/skills/engineering/resolve-issues/scripts/manual-invocation.test.mjs +0 -14
  188. package/skills/engineering/resolve-issues/scripts/next-actions-capacity.test.mjs +0 -149
  189. package/skills/engineering/resolve-issues/scripts/next-actions.test.mjs +0 -915
  190. package/skills/engineering/resolve-issues/scripts/plan-sync.test.mjs +0 -156
  191. package/skills/engineering/resolve-issues/scripts/preflight-models.test.mjs +0 -448
  192. package/skills/engineering/resolve-issues/scripts/ready-check.test.mjs +0 -217
  193. package/skills/engineering/resolve-issues/scripts/rehydrate.test.mjs +0 -91
  194. package/skills/engineering/resolve-issues/scripts/review-verdict.test.mjs +0 -1060
  195. package/skills/engineering/resolve-issues/scripts/size-check.test.mjs +0 -458
  196. package/skills/engineering/resolve-release/scripts/apply.test.mjs +0 -99
  197. package/skills/engineering/resolve-release/scripts/dark-flags.test.mjs +0 -100
  198. package/skills/engineering/resolve-release/scripts/flow.test.mjs +0 -340
  199. package/skills/engineering/resolve-release/scripts/green-head.test.mjs +0 -101
  200. package/skills/engineering/resolve-release/scripts/npm-version-absence.test.mjs +0 -426
  201. package/skills/engineering/resolve-release/scripts/plan-npm.test.mjs +0 -659
  202. package/skills/engineering/resolve-release/scripts/plan-toml-boundary.test.mjs +0 -317
  203. package/skills/engineering/resolve-release/scripts/plan-uv.test.mjs +0 -291
  204. package/skills/engineering/resolve-release/scripts/plan.test.mjs +0 -428
  205. package/skills/engineering/test-quality/scripts/lint-tests.test.mjs +0 -591
  206. package/skills/engineering/test-quality/scripts/mock-policy-config.test.mjs +0 -119
  207. package/skills/engineering/test-quality/scripts/persist-conventions.test.mjs +0 -104
  208. package/skills/risk-compliance/compliance-audit/scripts/compliance-audit.test.mjs +0 -14
  209. package/skills/risk-compliance/doc-authenticity/manual-invocation.test.mjs +0 -12
  210. package/skills/risk-compliance/doc-authenticity/scripts/validate_verdict.test.mjs +0 -212
  211. package/skills/risk-compliance/tld-detection/scripts/detect_ladders.test.mjs +0 -14
  212. package/skills/risk-compliance/tld-detection/scripts/detect_streams.test.mjs +0 -14
  213. package/skills/risk-compliance/tld-detection/tests/test_detect_ladders.py +0 -134
  214. package/skills/risk-compliance/tld-detection/tests/test_detect_streams.py +0 -520
@@ -1,428 +0,0 @@
1
- import { test } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
4
- import { createHash } from 'node:crypto'
5
- import { tmpdir } from 'node:os'
6
- import { join } from 'node:path'
7
- import { spawnSync } from 'node:child_process'
8
- import {
9
- bumpFor,
10
- classifySubject,
11
- latestReleaseTag,
12
- nextReleaseCandidateTag,
13
- nextVersion,
14
- parseSemver,
15
- planRelease,
16
- renderChangelog,
17
- } from './plan.mjs'
18
-
19
- function git(cwd, args) {
20
- const result = spawnSync('git', args, { cwd, encoding: 'utf8' })
21
- assert.equal(result.status, 0, result.stderr)
22
- return result.stdout
23
- }
24
-
25
- function repo() {
26
- const cwd = mkdtempSync(join(tmpdir(), 'rush-release-plan-'))
27
- git(cwd, ['init', '-b', 'main'])
28
- git(cwd, ['config', 'user.email', 'dev@example.com'])
29
- git(cwd, ['config', 'user.name', 'Dev'])
30
- git(cwd, ['config', 'commit.gpgsign', 'false'])
31
- writeFileSync(join(cwd, 'package.json'), `${JSON.stringify({ name: 'demo', version: '1.2.0' }, null, 2)}\n`)
32
- git(cwd, ['add', 'package.json'])
33
- git(cwd, ['commit', '-m', 'chore: init'])
34
- git(cwd, ['tag', 'v1.2.0'])
35
- return cwd
36
- }
37
-
38
- test('semver helpers', () => {
39
- assert.deepEqual(parseSemver('v1.2.3'), { major: 1, minor: 2, patch: 3 })
40
- assert.equal(parseSemver('v1.2.3-rc.1'), null)
41
- assert.equal(latestReleaseTag(['v1.2.0', 'v1.10.0', 'v1.2.3-rc.1']).tag, 'v1.10.0')
42
- assert.deepEqual(nextVersion({ major: 1, minor: 2, patch: 3 }, 'minor'), { major: 1, minor: 3, patch: 0 })
43
- assert.equal(nextReleaseCandidateTag('v1.3.0', []), 'v1.3.0-rc.1')
44
- assert.equal(nextReleaseCandidateTag('v1.3.0', ['v1.3.0-rc.1', 'v1.3.0-rc.3']), 'v1.3.0-rc.4')
45
- })
46
-
47
- test('conventional commits drive the bump, with 0.x breaking as minor', () => {
48
- const feat = [classifySubject('feat(api): list invoices')]
49
- const fix = [classifySubject('fix: null guard')]
50
- const breaking = [classifySubject('feat!: drop v1')]
51
- assert.equal(bumpFor(feat, { major: 1, minor: 2, patch: 0 }).bump, 'minor')
52
- assert.equal(bumpFor(fix, { major: 1, minor: 2, patch: 0 }).bump, 'patch')
53
- assert.equal(bumpFor(breaking, { major: 1, minor: 2, patch: 0 }).bump, 'major')
54
- assert.equal(bumpFor(breaking, { major: 0, minor: 9, patch: 0 }).bump, 'minor')
55
- assert.equal(bumpFor(breaking, { major: 0, minor: 9, patch: 0 }, { strict: true }).bump, 'major')
56
- })
57
-
58
- test('changelog groups notable commits and skips internals', () => {
59
- const md = renderChangelog({
60
- version: '1.3.0',
61
- date: '2026-08-24',
62
- changes: [
63
- classifySubject('feat: add export'),
64
- classifySubject('fix: timezone'),
65
- classifySubject('chore: ignore me'),
66
- ],
67
- })
68
- assert.match(md, /## \[1\.3\.0\] - 2026-08-24/)
69
- assert.match(md, /### Added\n- add export/)
70
- assert.match(md, /### Fixed\n- timezone/)
71
- assert.doesNotMatch(md, /ignore me/)
72
- })
73
-
74
- test('planRelease reads git history from the frozen SHA', () => {
75
- const cwd = repo()
76
- writeFileSync(join(cwd, 'a.txt'), 'a\n')
77
- git(cwd, ['add', 'a.txt'])
78
- git(cwd, ['commit', '-m', 'feat: export csv'])
79
- const sha = git(cwd, ['rev-parse', 'HEAD']).trim()
80
- writeFileSync(join(cwd, 'package.json'), `${JSON.stringify({ name: 'demo', version: '9.0.0' }, null, 2)}\n`)
81
- git(cwd, ['add', 'package.json'])
82
- git(cwd, ['commit', '-m', 'feat!: should not count if SHA is frozen earlier'])
83
- const plan = planRelease({ sha, cwd, now: new Date('2026-08-24T00:00:00Z') })
84
- assert.equal(plan.baselineTag, 'v1.2.0')
85
- assert.equal(plan.currentVersion, '1.2.0')
86
- assert.equal(plan.bump, 'minor')
87
- assert.equal(plan.nextVersion, '1.3.0')
88
- assert.equal(plan.tag, 'v1.3.0')
89
- assert.deepEqual(plan.versionFiles, ['package.json'])
90
- assert.match(plan.changelogMarkdown, /export csv/)
91
- assert.doesNotMatch(plan.changelogMarkdown, /should not count/)
92
- })
93
-
94
- test('Cloud Run plan reserves the next unused immutable candidate tag', () => {
95
- const cwd = repo()
96
- writeFileSync(join(cwd, 'a.txt'), 'a\n')
97
- git(cwd, ['add', 'a.txt'])
98
- git(cwd, ['commit', '-m', 'feat: export csv'])
99
- const sha = git(cwd, ['rev-parse', 'HEAD']).trim()
100
- git(cwd, ['tag', 'v1.3.0-rc.1'])
101
- git(cwd, ['tag', 'v1.3.0-rc.2'])
102
-
103
- const plan = planRelease({ sha, cloudRun: true, cwd, now: new Date('2026-08-24T00:00:00Z') })
104
-
105
- assert.equal(plan.tag, 'v1.3.0')
106
- assert.equal(plan.candidateTag, 'v1.3.0-rc.3')
107
- })
108
-
109
- test('planRelease ignores higher tags that are not reachable from the frozen SHA', () => {
110
- const cwd = repo()
111
- const candidate = git(cwd, ['rev-parse', 'HEAD']).trim()
112
- git(cwd, ['switch', '-c', 'unrelated'])
113
- writeFileSync(join(cwd, 'side.txt'), 'side\n')
114
- git(cwd, ['add', 'side.txt'])
115
- git(cwd, ['commit', '-m', 'feat: unrelated branch'])
116
- git(cwd, ['tag', 'v9.0.0'])
117
- git(cwd, ['switch', 'main'])
118
-
119
- const plan = planRelease({ sha: candidate, cwd, now: new Date('2026-08-24T00:00:00Z') })
120
- assert.equal(plan.baselineTag, 'v1.2.0')
121
- assert.equal(plan.currentVersion, '1.2.0')
122
- assert.equal(plan.nextVersion, '1.2.1')
123
- })
124
-
125
- test('planRelease uses the repository release identity when the private root package has no version', () => {
126
- const cwd = mkdtempSync(join(tmpdir(), 'rush-release-distribution-plan-'))
127
- git(cwd, ['init', '-b', 'main'])
128
- git(cwd, ['config', 'user.email', 'dev@example.com'])
129
- git(cwd, ['config', 'user.name', 'Dev'])
130
- git(cwd, ['config', 'commit.gpgsign', 'false'])
131
- mkdirSync(join(cwd, 'skills/distribution'), { recursive: true })
132
- writeFileSync(join(cwd, 'package.json'), `${JSON.stringify({ name: 'private-root', private: true }, null, 2)}\n`)
133
- writeFileSync(join(cwd, 'skills/distribution/manifest.json'), `${JSON.stringify({
134
- releaseVersion: '0.19.0',
135
- sources: [{
136
- id: 'first-party',
137
- renderTargets: [
138
- { id: 'private-checkout', revision: 'main' },
139
- ],
140
- }],
141
- }, null, 2)}\n`)
142
- git(cwd, ['add', '.'])
143
- git(cwd, ['commit', '-m', 'chore: release 0.19.0'])
144
- git(cwd, ['tag', 'v0.19.0'])
145
- writeFileSync(join(cwd, 'change.txt'), 'manual trigger only\n')
146
- git(cwd, ['add', 'change.txt'])
147
- git(cwd, ['commit', '-m', 'fix: make coverage skills manual only'])
148
-
149
- const plan = planRelease({
150
- sha: git(cwd, ['rev-parse', 'HEAD']).trim(),
151
- cwd,
152
- now: new Date('2026-08-30T00:00:00Z'),
153
- })
154
-
155
- assert.equal(plan.currentVersion, '0.19.0')
156
- assert.equal(plan.nextVersion, '0.19.1')
157
- assert.deepEqual(plan.versionFiles, ['skills/distribution/manifest.json'])
158
- })
159
-
160
- // rush-release never passes --dark-flags, so its plan must be exactly what it was before
161
- // dark launch existed: no darkFlags key and an unchanged changelog.
162
- test('a plan without dark flags carries no dark-launch trace', () => {
163
- const cwd = repo()
164
- git(cwd, ['commit', '--allow-empty', '-m', 'feat: export csv'])
165
- const plan = planRelease({ sha: 'HEAD', cwd, now: new Date('2026-08-24T00:00:00Z') })
166
- assert.equal('darkFlags' in plan, false)
167
- assert.equal(plan.changelogMarkdown, '## [1.3.0] - 2026-08-24\n\n### Added\n- export csv\n')
168
- })
169
-
170
- test('dark flags add a Shipped dark changelog section and keep the bump', () => {
171
- const cwd = repo()
172
- git(cwd, ['commit', '--allow-empty', '-m', 'feat: bulk refunds'])
173
- const darkFlags = [{ name: 'bulk-refunds', summary: 'Bulk refunds' }]
174
- const plan = planRelease({ sha: 'HEAD', cwd, darkFlags, now: new Date('2026-08-24T00:00:00Z') })
175
- assert.equal(plan.bump, 'minor')
176
- assert.deepEqual(plan.darkFlags, darkFlags)
177
- assert.equal(plan.changelogMarkdown,
178
- '## [1.3.0] - 2026-08-24\n\n### Added\n- bulk refunds\n\n### Shipped dark\n- Bulk refunds (flag `bulk-refunds`, off)\n')
179
- })
180
-
181
- test('the CLI reads dark flags from a dark-flags.mjs list file and rejects malformed ones', () => {
182
- const cwd = repo()
183
- git(cwd, ['commit', '--allow-empty', '-m', 'fix: rounding'])
184
- const good = join(cwd, 'flags.json')
185
- writeFileSync(good, JSON.stringify({ flags: [{ name: 'new-checkout', summary: 'New checkout', default: false }], errors: [] }))
186
- const script = join(import.meta.dirname, 'plan.mjs')
187
- const ok = spawnSync('node', [script, '--sha', 'HEAD', '--json', '--dark-flags', good], { cwd, encoding: 'utf8' })
188
- assert.equal(ok.status, 0, ok.stderr)
189
- assert.deepEqual(JSON.parse(ok.stdout).darkFlags, [{ name: 'new-checkout', summary: 'New checkout' }])
190
- const bad = join(cwd, 'bad.json')
191
- writeFileSync(bad, JSON.stringify([{ name: 'no-summary' }]))
192
- const refused = spawnSync('node', [script, '--sha', 'HEAD', '--dark-flags', bad], { cwd, encoding: 'utf8' })
193
- assert.equal(refused.status, 2)
194
- assert.match(refused.stderr, /name, summary/)
195
- })
196
-
197
- function preparedRepo(t, { productCut = false, badLockCut = false, python = false, badPackageCut = false } = {}) {
198
- const cwd = repo()
199
- const remote = mkdtempSync(join(tmpdir(), 'prepared-release-origin-'))
200
- t.after(() => { rmSync(cwd, { recursive: true, force: true }); rmSync(remote, { recursive: true, force: true }) })
201
- git(remote, ['init', '--bare'])
202
- // Exercise git's real transport while keeping the canonical origin identity offline.
203
- git(cwd, ['remote', 'add', 'origin', 'https://github.com/example/demo.git'])
204
- git(cwd, ['config', `url.${remote}.insteadOf`, 'https://github.com/example/demo.git'])
205
- const lock = (version) => ({ name: 'demo', version, lockfileVersion: 3, packages: { '': { name: 'demo', version } } })
206
- writeFileSync(join(cwd, 'package-lock.json'), JSON.stringify(lock('1.2.0')))
207
- writeFileSync(join(cwd, 'VERSION'), '1.2.0\n')
208
- if (python) writeFileSync(join(cwd, 'pyproject.toml'), '[project]\nname = "demo"\nversion = "1.2.0"\n\n[tool.other]\nversion = "not-a-release"\n')
209
- writeFileSync(join(cwd, 'CHANGELOG.md'), '# Changelog\n\n## [1.2.0] - 2026-08-01\n\n- Previous release.\n')
210
- git(cwd, ['add', '.'])
211
- git(cwd, ['commit', '-m', 'chore: existing identities'])
212
- git(cwd, ['switch', '-c', 'old-metadata'])
213
- writeFileSync(join(cwd, 'package.json'), JSON.stringify({ name: 'demo', version: '1.3.0', ...(badPackageCut ? { scripts: { build: 'execute-unreviewed-code' } } : {}) }))
214
- const cutLock = lock('1.3.0')
215
- if (badLockCut) cutLock.packages['node_modules/unsafe'] = { version: '9.0.0' }
216
- writeFileSync(join(cwd, 'package-lock.json'), JSON.stringify(cutLock))
217
- writeFileSync(join(cwd, 'VERSION'), '1.3.0\n')
218
- if (python) writeFileSync(join(cwd, 'pyproject.toml'), '[project]\nname = "demo"\nversion = "1.3.0"\n\n[tool.other]\nversion = "not-a-release"\n')
219
- const original = '## [1.3.0] - 2026-08-24\n\n### Added\n- Original notes.\n'
220
- writeFileSync(join(cwd, 'CHANGELOG.md'), `# Changelog\n\n${original}\n## [1.2.0] - 2026-08-01\n\n- Previous release.\n`)
221
- if (productCut) writeFileSync(join(cwd, 'product.js'), 'unreviewed code\n')
222
- git(cwd, ['add', '.'])
223
- git(cwd, ['commit', '-m', 'chore(release): 1.3.0'])
224
- const cutSha = git(cwd, ['rev-parse', 'HEAD']).trim()
225
- git(cwd, ['switch', 'main'])
226
- git(cwd, ['merge', '--no-ff', 'old-metadata', '-m', 'Merge reviewed metadata'])
227
- const mergeSha = git(cwd, ['rev-parse', 'HEAD']).trim()
228
- git(cwd, ['tag', '-a', 'v1.3.0-rc.1', '-m', 'Failed candidate'])
229
- git(cwd, ['push', 'origin', 'main', '--tags'])
230
- const reviewed = '## [1.3.0] - 2026-08-24\n\n### Added\n- Reviewed corrected notes.\n\n### Fixed\n- Candidate failure.\n'
231
- writeFileSync(join(cwd, 'CHANGELOG.md'), `# Changelog\n\n${reviewed}\n## [1.2.0] - 2026-08-01\n\n- Previous release.\n`)
232
- writeFileSync(join(cwd, 'product.js'), 'reviewed fix\n')
233
- git(cwd, ['add', '.'])
234
- git(cwd, ['commit', '-m', 'fix: candidate failure and release notes'])
235
- const sha = git(cwd, ['rev-parse', 'HEAD']).trim()
236
- git(cwd, ['push', 'origin', 'main'])
237
- const preparedMetadata = {
238
- schema: 'prepared-release-metadata/v1', repository: 'example/demo', version: '1.3.0', metadataPr: 42,
239
- cutSha, mergeSha,
240
- previousAttempt: { receiptId: 'failed-attempt-1', receiptSha256: 'a'.repeat(64), candidateTag: 'v1.3.0-rc.1', candidateSha: mergeSha },
241
- }
242
- return { cwd, remote, sha, preparedMetadata, original, reviewed }
243
- }
244
-
245
- function preparedPlan(fixture, opts = {}) {
246
- return planRelease({ ...fixture, cloudRun: true, now: new Date('2026-08-25T00:00:00Z'), ...opts })
247
- }
248
-
249
- const digest = (text) => createHash('sha256').update(text).digest('hex')
250
-
251
- test('prepared reuse preserves version and binds reviewed target notes separately from historical metadata', (t) => {
252
- const f = preparedRepo(t)
253
- const oldReceipt = join(f.cwd, '.git', 'resolve-release-authorization.json')
254
- writeFileSync(oldReceipt, 'historical receipt: must not be read or changed')
255
- const before = git(f.cwd, ['status', '--porcelain'])
256
- const plan = preparedPlan(f)
257
- assert.equal(plan.nextVersion, '1.3.0')
258
- assert.equal(plan.currentVersion, '1.3.0')
259
- assert.equal(plan.bump, 'none')
260
- assert.equal(plan.tag, 'v1.3.0')
261
- assert.equal(plan.candidateTag, 'v1.3.0-rc.2')
262
- assert.equal(plan.sha, f.sha)
263
- assert.equal(plan.releaseSourceSha, f.sha)
264
- assert.equal(plan.changelogMarkdown, f.reviewed)
265
- assert.equal(plan.date, '2026-08-24')
266
- assert.equal(plan.metadata.mode, 'reuse-prepared')
267
- assert.equal(plan.metadata.cutSha, f.preparedMetadata.cutSha)
268
- assert.equal(plan.metadata.mergeSha, f.preparedMetadata.mergeSha)
269
- assert.equal(plan.metadata.preparedSectionSha256, digest(f.reviewed))
270
- assert.equal(plan.metadata.originalSectionSha256, digest(f.original))
271
- assert.deepEqual(plan.metadata.previousAttempt, f.preparedMetadata.previousAttempt)
272
- assert.notEqual(plan.receiptId, f.preparedMetadata.previousAttempt.receiptId)
273
- assert.notEqual(preparedPlan(f).receiptId, plan.receiptId)
274
- assert.equal('permissions' in plan, false)
275
- assert.equal(readFileSync(oldReceipt, 'utf8'), 'historical receipt: must not be read or changed')
276
- assert.equal(git(f.cwd, ['status', '--porcelain']), before)
277
- // Neither a dirty worktree nor a later commit can replace the frozen source or notes.
278
- writeFileSync(join(f.cwd, 'VERSION'), '9.0.0\n')
279
- git(f.cwd, ['add', 'VERSION'])
280
- git(f.cwd, ['commit', '-m', 'feat!: unrelated later change'])
281
- assert.equal(preparedPlan(f).metadata.preparedSectionSha256, digest(f.reviewed))
282
- })
283
-
284
- test('prepared reuse uses remote ordinals even with stale local tags and never recycles the historical ordinal', (t) => {
285
- const f = preparedRepo(t)
286
- git(f.cwd, ['tag', 'v1.3.0-rc.4'])
287
- git(f.cwd, ['push', 'origin', 'refs/tags/v1.3.0-rc.4'])
288
- git(f.cwd, ['tag', '-d', 'v1.3.0-rc.4', 'v1.3.0-rc.1'])
289
- assert.equal(preparedPlan(f).candidateTag, 'v1.3.0-rc.5')
290
- git(f.remote, ['update-ref', '-d', 'refs/tags/v1.3.0-rc.4'])
291
- git(f.remote, ['update-ref', '-d', 'refs/tags/v1.3.0-rc.1'])
292
- assert.equal(preparedPlan(f).candidateTag, 'v1.3.0-rc.2')
293
- })
294
-
295
- test('prepared reuse blocks a remote final tag, inspection errors and noncanonical or wrong repository identity', (t) => {
296
- const f = preparedRepo(t)
297
- git(f.cwd, ['tag', '-a', 'v1.3.0', '-m', 'Already used'])
298
- git(f.cwd, ['push', 'origin', 'refs/tags/v1.3.0'])
299
- git(f.cwd, ['tag', '-d', 'v1.3.0'])
300
- assert.throws(() => preparedPlan(f), /final tag.*already exists/)
301
- git(f.remote, ['update-ref', '-d', 'refs/tags/v1.3.0'])
302
- git(f.cwd, ['config', '--unset', `url.${f.remote}.insteadOf`])
303
- // Repoint only the transport rewrite; origin's canonical identity stays valid.
304
- git(f.cwd, ['config', `url.${join(f.remote, 'missing')}.insteadOf`, 'https://github.com/example/demo.git'])
305
- assert.throws(() => preparedPlan(f), /remote inspection failed/)
306
- git(f.cwd, ['remote', 'set-url', 'origin', f.remote])
307
- assert.throws(() => preparedPlan(f), /canonical GitHub origin/)
308
- git(f.cwd, ['remote', 'set-url', 'origin', 'https://github.com/other/demo.git'])
309
- assert.throws(() => preparedPlan(f), /repository.*does not match/)
310
- })
311
-
312
- test('prepared reuse refuses product changes hidden in the original metadata cut', (t) => {
313
- for (const options of [{ productCut: true }, { badLockCut: true }, { badPackageCut: true }]) {
314
- const f = preparedRepo(t, options)
315
- assert.throws(() => preparedPlan(f), /metadata-only/)
316
- }
317
- })
318
-
319
- test('prepared reuse validates schema, full identities, previous attempt binding and unchanged version identity', (t) => {
320
- const f = preparedRepo(t)
321
- const p = f.preparedMetadata
322
- const cases = [
323
- [{ ...p, schema: 'future/v2' }, /schema/],
324
- [{ ...p, version: 'v1.3.0' }, /version/],
325
- [{ ...p, version: '1.4.0', previousAttempt: { ...p.previousAttempt, candidateTag: 'v1.4.0-rc.1' } }, /version.*does not match/],
326
- [{ ...p, metadataPr: 0 }, /metadataPr/],
327
- [{ ...p, mergeSha: 'HEAD' }, /full.*SHA/],
328
- [{ ...p, cutSha: p.cutSha.slice(0, 12) }, /full.*SHA/],
329
- [{ ...p, extraPermission: true }, /unknown field/],
330
- [{ ...p, previousAttempt: { ...p.previousAttempt, receiptSha256: '' } }, /receiptSha256/],
331
- [{ ...p, previousAttempt: { ...p.previousAttempt, candidateSha: f.sha } }, /previous.*candidateSha/],
332
- [{ ...p, previousAttempt: { ...p.previousAttempt, candidateTag: 'v1.4.0-rc.1' } }, /previous.*candidateTag/],
333
- ]
334
- for (const [preparedMetadata, error] of cases) assert.throws(() => preparedPlan(f, { preparedMetadata }), error)
335
- assert.throws(() => preparedPlan(f, { sha: 'HEAD' }), /full.*SHA/)
336
- assert.throws(() => preparedPlan(f, { trunk: 'release' }), /trunk.*main/)
337
- assert.throws(() => preparedPlan(f, { darkFlags: [{ name: 'flag', summary: 'Unreviewed notes' }] }), /dark flags/)
338
- })
339
-
340
- test('prepared reuse rejects unequal merge trees, unrelated provenance and missing commit objects', (t) => {
341
- const f = preparedRepo(t)
342
- const p = f.preparedMetadata
343
- const unequal = { ...p, mergeSha: f.sha, previousAttempt: { ...p.previousAttempt, candidateSha: f.sha } }
344
- assert.throws(() => preparedPlan(f, { preparedMetadata: unequal }), /cut and merge trees/)
345
- git(f.cwd, ['switch', '--orphan', 'unrelated'])
346
- git(f.cwd, ['add', '.'])
347
- git(f.cwd, ['commit', '--allow-empty', '-m', 'unrelated root'])
348
- const unrelated = git(f.cwd, ['rev-parse', 'HEAD']).trim()
349
- assert.throws(() => preparedPlan(f, { sha: unrelated }), /merge.*ancestor/)
350
- assert.throws(() => preparedPlan(f, { sha: 'f'.repeat(40) }), /failed|object|revision/i)
351
- })
352
-
353
- test('prepared reuse rejects target identity drift, duplicate or absent release notes and a rebound old RC', (t) => {
354
- const f = preparedRepo(t)
355
- const checkChange = (file, text, error) => {
356
- git(f.cwd, ['reset', '--hard', f.sha])
357
- writeFileSync(join(f.cwd, file), text)
358
- git(f.cwd, ['add', file])
359
- git(f.cwd, ['commit', '-m', 'fix: invalid prepared identity'])
360
- const sha = git(f.cwd, ['rev-parse', 'HEAD']).trim()
361
- assert.throws(() => preparedPlan(f, { sha }), error)
362
- }
363
- checkChange('VERSION', '1.4.0\n', /version.*does not match/)
364
- checkChange('package-lock.json', JSON.stringify({ name: 'demo', version: '1.3.0', packages: { '': { version: '1.2.0' } } }), /root lock.*version/)
365
- checkChange('package-lock.json', JSON.stringify({ name: 'foreign', version: '1.3.0', packages: { '': { version: '1.3.0' } } }), /root lock.*name/)
366
- checkChange('CHANGELOG.md', `${f.reviewed}\n${f.reviewed}`, /exactly one/)
367
- checkChange('CHANGELOG.md', '# Changelog\n', /exactly one/)
368
- git(f.remote, ['update-ref', 'refs/tags/v1.3.0-rc.1', f.sha])
369
- assert.throws(() => preparedPlan(f), /previous.*remote.*SHA/)
370
- })
371
-
372
- test('prepared reuse validates Python project identity without confusing tool versions, and supports artifact-only plans', (t) => {
373
- const f = preparedRepo(t, { python: true })
374
- const plan = preparedPlan(f, { cloudRun: false })
375
- assert.equal(plan.candidateTag, null)
376
- assert.equal(plan.bump, 'none')
377
- assert.deepEqual(plan.versionFiles, ['package.json', 'package-lock.json', 'pyproject.toml', 'VERSION'])
378
- writeFileSync(join(f.cwd, 'pyproject.toml'), '[project]\nversion = "1.4.0"\n')
379
- git(f.cwd, ['add', '.'])
380
- git(f.cwd, ['commit', '-m', 'fix: mismatched python identity'])
381
- assert.throws(() => preparedPlan(f, { sha: git(f.cwd, ['rev-parse', 'HEAD']).trim() }), /pyproject.toml version.*does not match/)
382
- })
383
-
384
- test('prepared reuse refuses distribution cuts instead of trusting a broad generated-file allowlist', (t) => {
385
- const f = preparedRepo(t)
386
- mkdirSync(join(f.cwd, 'skills/distribution'), { recursive: true })
387
- writeFileSync(join(f.cwd, 'skills/distribution/manifest.json'), JSON.stringify({ releaseVersion: '1.3.0' }))
388
- git(f.cwd, ['add', '.'])
389
- git(f.cwd, ['commit', '-m', 'chore: distribution identity'])
390
- const cutSha = git(f.cwd, ['rev-parse', 'HEAD']).trim()
391
- const preparedMetadata = { ...f.preparedMetadata, cutSha, mergeSha: cutSha,
392
- previousAttempt: { ...f.preparedMetadata.previousAttempt, candidateSha: cutSha } }
393
- assert.throws(() => preparedPlan(f, { sha: cutSha, preparedMetadata }), /distribution-aware.*unsupported/)
394
- })
395
-
396
- test('prepared reuse blocks an RC collision introduced between remote discovery and the exact absence check', (t) => {
397
- const f = preparedRepo(t)
398
- const uploadPack = join(f.cwd, '.git', 'upload-pack.sh')
399
- writeFileSync(uploadPack, `#!/bin/sh\ngit-upload-pack "$@"\nstatus=$?\ngit --git-dir='${f.remote}' update-ref refs/tags/v1.3.0-rc.2 ${f.sha}\nexit "$status"\n`, { mode: 0o755 })
400
- git(f.cwd, ['config', 'remote.origin.uploadpack', uploadPack])
401
- assert.throws(() => preparedPlan(f), /proposed candidate tag already exists remotely/)
402
- })
403
-
404
- test('prepared reuse blocks an exact remote inspection failure after successful discovery', (t) => {
405
- const f = preparedRepo(t)
406
- const uploadPack = join(f.cwd, '.git', 'upload-pack.sh')
407
- const marker = join(f.cwd, '.git', 'inspected')
408
- writeFileSync(uploadPack, `#!/bin/sh\nif test -e '${marker}'; then exit 1; fi\ntouch '${marker}'\nexec git-upload-pack "$@"\n`, { mode: 0o755 })
409
- git(f.cwd, ['config', 'remote.origin.uploadpack', uploadPack])
410
- assert.throws(() => preparedPlan(f), /remote inspection failed/)
411
- })
412
-
413
- test('CLI loads prepared metadata without applying it and rejects missing, malformed and unknown options', (t) => {
414
- const f = preparedRepo(t)
415
- const file = join(f.cwd, '.git', 'prepared.json')
416
- writeFileSync(file, JSON.stringify(f.preparedMetadata))
417
- const script = join(import.meta.dirname, 'plan.mjs')
418
- const run = (...args) => spawnSync(process.execPath, [script, '--sha', f.sha, '--json', ...args], { cwd: f.cwd, encoding: 'utf8' })
419
- const result = run('--prepared-metadata', file, '--cloud-run')
420
- assert.equal(result.status, 0, result.stderr)
421
- assert.equal(JSON.parse(result.stdout).sha, f.sha)
422
- assert.equal(JSON.parse(result.stdout).bump, 'none')
423
- assert.equal(JSON.parse(result.stdout).metadata.preparedSectionSha256, digest(f.reviewed))
424
- assert.equal(run('--prepared-metadata').status, 2)
425
- writeFileSync(file, '{')
426
- assert.notEqual(run('--prepared-metadata', file).status, 0)
427
- assert.equal(run('--version', '9.0.0').status, 2)
428
- })