@1aboveio/skills 1.0.7 → 1.0.9

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 (224) 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 +92 -34
  8. package/skills/app-dev/android-foundation/assets/android-ci-job.yml +13 -7
  9. package/skills/app-dev/android-foundation/references/ci.md +23 -13
  10. package/skills/app-dev/android-foundation/references/delivery-focus.md +29 -0
  11. package/skills/app-dev/android-foundation/references/diagnosis.md +136 -0
  12. package/skills/app-dev/android-foundation/references/gotchas.md +20 -11
  13. package/skills/app-dev/android-foundation/references/setup.md +15 -6
  14. package/skills/app-dev/android-foundation/release-check.mjs +3 -1
  15. package/skills/app-dev/android-foundation/scripts/device-run.sh +148 -42
  16. package/skills/app-dev/app-debug/SKILL.md +1 -1
  17. package/skills/app-dev/app-debug/release-check.mjs +3 -1
  18. package/skills/app-dev/better-auth/SKILL.md +1 -1
  19. package/skills/app-dev/better-auth/release-check.mjs +3 -1
  20. package/skills/app-dev/data-catalog/SKILL.md +1 -1
  21. package/skills/app-dev/data-catalog/release-check.mjs +3 -1
  22. package/skills/app-dev/data-quality/SKILL.md +1 -1
  23. package/skills/app-dev/data-quality/release-check.mjs +3 -1
  24. package/skills/app-dev/ios-foundation/SKILL.md +1 -1
  25. package/skills/app-dev/ios-foundation/release-check.mjs +3 -1
  26. package/skills/app-dev/monorepo/SKILL.md +1 -1
  27. package/skills/app-dev/monorepo/release-check.mjs +3 -1
  28. package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
  29. package/skills/app-dev/nextjs-fullstack/release-check.mjs +3 -1
  30. package/skills/app-dev/prisma-setup/SKILL.md +1 -1
  31. package/skills/app-dev/prisma-setup/release-check.mjs +3 -1
  32. package/skills/app-dev/pyspark/SKILL.md +1 -1
  33. package/skills/app-dev/pyspark/release-check.mjs +3 -1
  34. package/skills/app-dev/python-backend/SKILL.md +1 -1
  35. package/skills/app-dev/python-backend/release-check.mjs +3 -1
  36. package/skills/app-dev/shadcn/SKILL.md +1 -1
  37. package/skills/app-dev/shadcn/release-check.mjs +3 -1
  38. package/skills/app-dev/zod-v4/SKILL.md +1 -1
  39. package/skills/app-dev/zod-v4/release-check.mjs +3 -1
  40. package/skills/cicd/cloud-build/SKILL.md +1 -1
  41. package/skills/cicd/cloud-build/release-check.mjs +3 -1
  42. package/skills/cicd/cloud-debug/SKILL.md +1 -1
  43. package/skills/cicd/cloud-debug/release-check.mjs +3 -1
  44. package/skills/cicd/cloud-deploy/SKILL.md +1 -1
  45. package/skills/cicd/cloud-deploy/release-check.mjs +3 -1
  46. package/skills/cicd/docker/SKILL.md +1 -1
  47. package/skills/cicd/docker/release-check.mjs +3 -1
  48. package/skills/cicd/github-actions/SKILL.md +1 -1
  49. package/skills/cicd/github-actions/release-check.mjs +3 -1
  50. package/skills/cicd/github-flow/SKILL.md +1 -1
  51. package/skills/cicd/github-flow/release-check.mjs +3 -1
  52. package/skills/cicd/google-cloud/SKILL.md +1 -1
  53. package/skills/cicd/google-cloud/release-check.mjs +3 -1
  54. package/skills/cicd/mergify/SKILL.md +1 -1
  55. package/skills/cicd/mergify/release-check.mjs +3 -1
  56. package/skills/cicd/podman/SKILL.md +1 -1
  57. package/skills/cicd/podman/release-check.mjs +3 -1
  58. package/skills/content/article-review/SKILL.md +1 -1
  59. package/skills/content/article-review/release-check.mjs +3 -1
  60. package/skills/content/article-to-image/SKILL.md +1 -1
  61. package/skills/content/article-to-image/release-check.mjs +3 -1
  62. package/skills/content/flowchart-svg/SKILL.md +1 -1
  63. package/skills/content/flowchart-svg/release-check.mjs +3 -1
  64. package/skills/content/image-gen/SKILL.md +1 -1
  65. package/skills/content/image-gen/release-check.mjs +3 -1
  66. package/skills/content/mermaid/SKILL.md +1 -1
  67. package/skills/content/mermaid/release-check.mjs +3 -1
  68. package/skills/content/wechat-article-writer/SKILL.md +1 -1
  69. package/skills/content/wechat-article-writer/release-check.mjs +3 -1
  70. package/skills/content/wechat-publisher/SKILL.md +1 -1
  71. package/skills/content/wechat-publisher/release-check.mjs +3 -1
  72. package/skills/content/wechat-search/SKILL.md +1 -1
  73. package/skills/content/wechat-search/release-check.mjs +3 -1
  74. package/skills/content/xai-search/SKILL.md +88 -13
  75. package/skills/content/xai-search/release-check.mjs +3 -1
  76. package/skills/content/xai-search/scripts/xai_search.sh +125 -149
  77. package/skills/core/distill-writing/SKILL.md +1 -1
  78. package/skills/core/distill-writing/release-check.mjs +3 -1
  79. package/skills/core/doc-to-md/SKILL.md +1 -1
  80. package/skills/core/doc-to-md/release-check.mjs +3 -1
  81. package/skills/core/editorial-review/SKILL.md +1 -1
  82. package/skills/core/editorial-review/release-check.mjs +3 -1
  83. package/skills/core/house-style/SKILL.md +1 -1
  84. package/skills/core/house-style/release-check.mjs +3 -1
  85. package/skills/core/plain-language/SKILL.md +1 -1
  86. package/skills/core/plain-language/release-check.mjs +3 -1
  87. package/skills/core/polish-cn/SKILL.md +1 -1
  88. package/skills/core/polish-cn/release-check.mjs +3 -1
  89. package/skills/core/reg-parser/SKILL.md +1 -1
  90. package/skills/core/reg-parser/release-check.mjs +3 -1
  91. package/skills/core/terminology/SKILL.md +1 -1
  92. package/skills/core/terminology/release-check.mjs +3 -1
  93. package/skills/core/write-doc/SKILL.md +1 -1
  94. package/skills/core/write-doc/release-check.mjs +3 -1
  95. package/skills/engineering/better-review/SKILL.md +1 -1
  96. package/skills/engineering/better-review/release-check.mjs +3 -1
  97. package/skills/engineering/contributing/SKILL.md +1 -1
  98. package/skills/engineering/contributing/release-check.mjs +3 -1
  99. package/skills/engineering/e2e-test/SKILL.md +1 -1
  100. package/skills/engineering/e2e-test/release-check.mjs +3 -1
  101. package/skills/engineering/engineering-runtime/SKILL.md +1 -1
  102. package/skills/engineering/engineering-runtime/coherence/workflow.json +13 -13
  103. package/skills/engineering/engineering-runtime/release-check.mjs +3 -1
  104. package/skills/engineering/engineering-runtime/scripts/installation-resolver.mjs +191 -0
  105. package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +51 -10
  106. package/skills/engineering/ensure-coverage/SKILL.md +1 -1
  107. package/skills/engineering/ensure-coverage/release-check.mjs +3 -1
  108. package/skills/engineering/golden-path/SKILL.md +1 -1
  109. package/skills/engineering/golden-path/release-check.mjs +3 -1
  110. package/skills/engineering/harness-runtime/SKILL.md +1 -1
  111. package/skills/engineering/harness-runtime/release-check.mjs +3 -1
  112. package/skills/engineering/resolve-issues/SKILL.md +11 -20
  113. package/skills/engineering/resolve-issues/references/delivery-progress.md +63 -0
  114. package/skills/engineering/resolve-issues/references/expire.md +24 -11
  115. package/skills/engineering/resolve-issues/references/implementation.md +9 -1
  116. package/skills/engineering/resolve-issues/references/preflight.md +7 -0
  117. package/skills/engineering/resolve-issues/references/shared-modules.md +27 -0
  118. package/skills/engineering/resolve-issues/references/task-plan.md +12 -5
  119. package/skills/engineering/resolve-issues/release-check.mjs +3 -1
  120. package/skills/engineering/resolve-issues/scripts/active-installation.mjs +32 -0
  121. package/skills/engineering/resolve-issues/scripts/install-hooks.mjs +3 -0
  122. package/skills/engineering/resolve-issues/scripts/rehydrate.mjs +17 -1
  123. package/skills/engineering/resolve-release/SKILL.md +1 -1
  124. package/skills/engineering/resolve-release/release-check.mjs +3 -1
  125. package/skills/engineering/rush-issues/SKILL.md +5 -2
  126. package/skills/engineering/rush-issues/release-check.mjs +3 -1
  127. package/skills/engineering/rush-release/SKILL.md +1 -1
  128. package/skills/engineering/rush-release/release-check.mjs +3 -1
  129. package/skills/engineering/test-quality/SKILL.md +1 -1
  130. package/skills/engineering/test-quality/release-check.mjs +3 -1
  131. package/skills/risk-compliance/compliance-audit/SKILL.md +1 -1
  132. package/skills/risk-compliance/compliance-audit/release-check.mjs +3 -1
  133. package/skills/risk-compliance/contract-compare/SKILL.md +1 -1
  134. package/skills/risk-compliance/contract-compare/release-check.mjs +3 -1
  135. package/skills/risk-compliance/contract-review/SKILL.md +1 -1
  136. package/skills/risk-compliance/contract-review/release-check.mjs +3 -1
  137. package/skills/risk-compliance/doc-authenticity/SKILL.md +1 -1
  138. package/skills/risk-compliance/doc-authenticity/release-check.mjs +3 -1
  139. package/skills/risk-compliance/fraud-analysis/SKILL.md +1 -1
  140. package/skills/risk-compliance/fraud-analysis/release-check.mjs +3 -1
  141. package/skills/risk-compliance/payment-analysis/SKILL.md +1 -1
  142. package/skills/risk-compliance/payment-analysis/release-check.mjs +3 -1
  143. package/skills/risk-compliance/similar-domain/SKILL.md +1 -1
  144. package/skills/risk-compliance/similar-domain/release-check.mjs +3 -1
  145. package/skills/risk-compliance/tld-detection/SKILL.md +1 -1
  146. package/skills/risk-compliance/tld-detection/release-check.mjs +3 -1
  147. package/skills/app-dev/android-foundation/scripts/device-run.test.mjs +0 -172
  148. package/skills/app-dev/data-catalog/scripts/data-catalog.test.mjs +0 -36
  149. package/skills/app-dev/data-quality/scripts/data-quality.test.mjs +0 -12
  150. package/skills/cicd/github-actions/scripts/ci-run-audit.test.mjs +0 -637
  151. package/skills/cicd/github-flow/scripts/flow.test.mjs +0 -114
  152. package/skills/cicd/github-flow/scripts/persist-conventions.test.mjs +0 -108
  153. package/skills/cicd/mergify/scripts/audit.test.mjs +0 -201
  154. package/skills/cicd/mergify/scripts/entrypoints.test.mjs +0 -106
  155. package/skills/cicd/mergify/scripts/pr-620-mergify-comments.fixture.json +0 -30
  156. package/skills/cicd/mergify/scripts/stack-push.test.mjs +0 -233
  157. package/skills/cicd/mergify/scripts/watch-pr-delivery.test.mjs +0 -1826
  158. package/skills/content/image-gen/scripts/credentials.test.mjs +0 -212
  159. package/skills/content/image-gen/scripts/options.test.mjs +0 -245
  160. package/skills/content/xai-search/tests/smoke_test.sh +0 -36
  161. package/skills/core/polish-cn/scripts/polish-cn.test.mjs +0 -244
  162. package/skills/core/reg-parser/scripts/reg-parser.test.mjs +0 -165
  163. package/skills/engineering/better-review/scripts/pr-context.test.mjs +0 -168
  164. package/skills/engineering/better-review/scripts/rereview-scope.test.mjs +0 -587
  165. package/skills/engineering/better-review/scripts/scan-diff.test.mjs +0 -414
  166. package/skills/engineering/better-review/scripts/vendor-sync.test.mjs +0 -96
  167. package/skills/engineering/contributing/scripts/persist-conventions.test.mjs +0 -118
  168. package/skills/engineering/e2e-test/scripts/presentation-checks.test.mjs +0 -106
  169. package/skills/engineering/e2e-test/scripts/presentation-perceivability.test.mjs +0 -117
  170. package/skills/engineering/e2e-test/scripts/presentation-reachability.test.mjs +0 -69
  171. package/skills/engineering/e2e-test/scripts/presentation-render-health.test.mjs +0 -98
  172. package/skills/engineering/e2e-test/scripts/presentation-sweep.test.mjs +0 -71
  173. package/skills/engineering/e2e-test/scripts/presentation-temporal.test.mjs +0 -59
  174. package/skills/engineering/e2e-test/scripts/project-route-manifest.test.mjs +0 -76
  175. package/skills/engineering/engineering-runtime/engineering-runtime.test.mjs +0 -68
  176. package/skills/engineering/engineering-runtime/scripts/invocation-policy.test.mjs +0 -95
  177. package/skills/engineering/engineering-runtime/scripts/main-module.test.mjs +0 -288
  178. package/skills/engineering/engineering-runtime/scripts/required-check-plan.test.mjs +0 -96
  179. package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.test.mjs +0 -96
  180. package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.test.mjs +0 -47
  181. package/skills/engineering/ensure-coverage/scripts/ci-audit.test.mjs +0 -86
  182. package/skills/engineering/ensure-coverage/scripts/coverage-ledger.test.mjs +0 -447
  183. package/skills/engineering/ensure-coverage/scripts/design-parity.test.mjs +0 -552
  184. package/skills/engineering/ensure-coverage/scripts/lint-tests.test.mjs +0 -289
  185. package/skills/engineering/ensure-coverage/scripts/mock-policy-config.test.mjs +0 -119
  186. package/skills/engineering/golden-path/scripts/golden-path.test.mjs +0 -1457
  187. package/skills/engineering/harness-runtime/discover-models.test.mjs +0 -363
  188. package/skills/engineering/harness-runtime/harness-runtime.test.mjs +0 -967
  189. package/skills/engineering/harness-runtime/model-catalog-seed.test.mjs +0 -829
  190. package/skills/engineering/harness-runtime/model-catalog.test.mjs +0 -366
  191. package/skills/engineering/resolve-issues/scripts/cleanup-runs.test.mjs +0 -280
  192. package/skills/engineering/resolve-issues/scripts/dropbox.test.mjs +0 -346
  193. package/skills/engineering/resolve-issues/scripts/findings-reuse.test.mjs +0 -88
  194. package/skills/engineering/resolve-issues/scripts/github-reviews.test.mjs +0 -264
  195. package/skills/engineering/resolve-issues/scripts/install-hooks.test.mjs +0 -79
  196. package/skills/engineering/resolve-issues/scripts/issue-tree.test.mjs +0 -107
  197. package/skills/engineering/resolve-issues/scripts/manual-invocation.test.mjs +0 -14
  198. package/skills/engineering/resolve-issues/scripts/next-actions-capacity.test.mjs +0 -149
  199. package/skills/engineering/resolve-issues/scripts/next-actions.test.mjs +0 -915
  200. package/skills/engineering/resolve-issues/scripts/plan-sync.test.mjs +0 -156
  201. package/skills/engineering/resolve-issues/scripts/preflight-models.test.mjs +0 -448
  202. package/skills/engineering/resolve-issues/scripts/ready-check.test.mjs +0 -217
  203. package/skills/engineering/resolve-issues/scripts/rehydrate.test.mjs +0 -91
  204. package/skills/engineering/resolve-issues/scripts/review-verdict.test.mjs +0 -1060
  205. package/skills/engineering/resolve-issues/scripts/size-check.test.mjs +0 -458
  206. package/skills/engineering/resolve-release/scripts/apply.test.mjs +0 -99
  207. package/skills/engineering/resolve-release/scripts/dark-flags.test.mjs +0 -100
  208. package/skills/engineering/resolve-release/scripts/flow.test.mjs +0 -340
  209. package/skills/engineering/resolve-release/scripts/green-head.test.mjs +0 -101
  210. package/skills/engineering/resolve-release/scripts/npm-version-absence.test.mjs +0 -426
  211. package/skills/engineering/resolve-release/scripts/plan-npm.test.mjs +0 -659
  212. package/skills/engineering/resolve-release/scripts/plan-toml-boundary.test.mjs +0 -317
  213. package/skills/engineering/resolve-release/scripts/plan-uv.test.mjs +0 -291
  214. package/skills/engineering/resolve-release/scripts/plan.test.mjs +0 -428
  215. package/skills/engineering/test-quality/scripts/lint-tests.test.mjs +0 -591
  216. package/skills/engineering/test-quality/scripts/mock-policy-config.test.mjs +0 -119
  217. package/skills/engineering/test-quality/scripts/persist-conventions.test.mjs +0 -104
  218. package/skills/risk-compliance/compliance-audit/scripts/compliance-audit.test.mjs +0 -14
  219. package/skills/risk-compliance/doc-authenticity/manual-invocation.test.mjs +0 -12
  220. package/skills/risk-compliance/doc-authenticity/scripts/validate_verdict.test.mjs +0 -212
  221. package/skills/risk-compliance/tld-detection/scripts/detect_ladders.test.mjs +0 -14
  222. package/skills/risk-compliance/tld-detection/scripts/detect_streams.test.mjs +0 -14
  223. package/skills/risk-compliance/tld-detection/tests/test_detect_ladders.py +0 -134
  224. 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
- })