@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,346 +0,0 @@
1
- import { test } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import { execFileSync, spawnSync } from 'node:child_process'
4
- import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs'
5
- import { tmpdir } from 'node:os'
6
- import { dirname, join } from 'node:path'
7
- import { fileURLToPath } from 'node:url'
8
-
9
- import { addEntry, changedRanges, emptyDropbox, fingerprint, fromReview, groupEntries, inUnitDiff, issueFor, markEntry, pendingRuns, planPost, post, sanitize } from './dropbox.mjs'
10
-
11
- const script = join(dirname(fileURLToPath(import.meta.url)), 'dropbox.mjs')
12
- const tmp = () => mkdtempSync(join(tmpdir(), 'dropbox-'))
13
-
14
- // A repository on `main` with a.ts (10 lines) and a unit branch that edits line 3 and adds b.ts.
15
- function unitRepo() {
16
- const dir = tmp()
17
- const git = (...args) => execFileSync('git', ['-C', dir, ...args], { encoding: 'utf8' })
18
- git('init', '-q', '-b', 'main')
19
- git('config', 'user.email', 't@example.com')
20
- git('config', 'user.name', 't')
21
- writeFileSync(join(dir, 'a.ts'), Array.from({ length: 10 }, (_, i) => `line ${i + 1}`).join('\n') + '\n')
22
- git('add', '.')
23
- git('commit', '-qm', 'base')
24
- git('checkout', '-qb', 'unit')
25
- const lines = readFileSync(join(dir, 'a.ts'), 'utf8').split('\n')
26
- lines[2] = 'changed 3'
27
- writeFileSync(join(dir, 'a.ts'), lines.join('\n'))
28
- writeFileSync(join(dir, 'b.ts'), 'new\n')
29
- git('add', '.')
30
- git('commit', '-qm', 'unit')
31
- return dir
32
- }
33
-
34
- const entry = (over = {}) => ({ unit: '12', source: 'review', kind: 'docs', file: 'docs/a.md', line: 4, summary: 'typo in heading', ...over })
35
-
36
- test('hunk ranges come from the new side; a pure deletion adds none', () => {
37
- assert.deepEqual(changedRanges('@@ -3 +3 @@\n@@ -7,0 +8,2 @@\n@@ -12,3 +13,0 @@\n'), [[3, 3], [8, 9]])
38
- })
39
-
40
- test('the unit diff covers changed lines and added files, not untouched lines', () => {
41
- const dir = unitRepo()
42
- assert.equal(inUnitDiff({ dir, base: 'main', file: 'a.ts', line: 3 }), true)
43
- assert.equal(inUnitDiff({ dir, base: 'main', file: 'a.ts', line: 7 }), false)
44
- assert.equal(inUnitDiff({ dir, base: 'main', file: 'a.ts' }), true, 'without a line, any change to the file counts')
45
- assert.equal(inUnitDiff({ dir, base: 'main', file: 'b.ts', line: 1 }), true)
46
- })
47
-
48
- test('uncommitted work in the workspace counts as the unit\'s change', () => {
49
- const dir = unitRepo()
50
- const lines = readFileSync(join(dir, 'a.ts'), 'utf8').split('\n')
51
- lines[8] = 'edited 9'
52
- writeFileSync(join(dir, 'a.ts'), lines.join('\n'))
53
- assert.equal(inUnitDiff({ dir, base: 'main', file: 'a.ts', line: 9 }), true)
54
- })
55
-
56
- test('check exits 0 and reports inDiff true inside the diff and false outside', () => {
57
- const dir = unitRepo()
58
- const inside = spawnSync('node', [script, 'check', '--git', dir, '--base', 'main', '--file', 'a.ts', '--line', '3'], { encoding: 'utf8' })
59
- assert.equal(inside.status, 0, inside.stderr)
60
- assert.deepEqual([JSON.parse(inside.stdout).inDiff, JSON.parse(inside.stdout).do], [true, 'fix it in this unit'])
61
- const outside = spawnSync('node', [script, 'check', '--git', dir, '--base', 'main', '--file', 'a.ts', '--line', '7'], { encoding: 'utf8' })
62
- assert.equal(outside.status, 0, outside.stderr)
63
- assert.equal(JSON.parse(outside.stdout).inDiff, false)
64
- })
65
-
66
- test('an implementer entry inside its own diff is refused; a review entry there is kept and marked', () => {
67
- const box = emptyDropbox()
68
- assert.equal(addEntry(box, entry({ source: 'implementer' }), true).refused, true)
69
- assert.equal(box.entries.length, 0)
70
- const kept = addEntry(box, entry({ source: 'review', finding: 'R2', severity: 'P2' }), true)
71
- assert.equal(kept.inDiff, true)
72
- assert.equal(box.entries[0].inDiff, true)
73
- })
74
-
75
- test('a P3 is skipped: add refuses it and from-review never adds it', () => {
76
- const box = emptyDropbox()
77
- assert.throws(() => addEntry(box, entry({ source: 'review', finding: 'R3', severity: 'P3' }), false), /a P3 is skipped/)
78
- const review = { unit: '12', findings: [{ id: 'R3', severity: 'P3', summary: 'tidy comment', file: 'a.ts', line: 2 }] }
79
- assert.deepEqual(fromReview(box, review), [])
80
- assert.equal(review.findings[0].dropboxId, undefined)
81
- assert.equal(box.entries.length, 0)
82
- })
83
-
84
- test('the CLI add refuses an implementer entry inside the diff with `refused` (exit 0) and writes nothing', () => {
85
- const dir = unitRepo()
86
- const path = join(tmp(), 'dropbox.json')
87
- const args = ['add', '--dropbox', path, '--unit', '7', '--source', 'implementer', '--kind', 'bug', '--file', 'a.ts', '--line', '3', '--summary', 's', '--git', dir, '--base', 'main']
88
- const refused = spawnSync('node', [script, ...args], { encoding: 'utf8' })
89
- assert.equal(refused.status, 0, refused.stderr)
90
- assert.ok(JSON.parse(refused.stdout).refused)
91
- assert.equal(existsSync(path), false)
92
- const ok = spawnSync('node', [script, ...args.map((a) => (a === '3' ? '7' : a))], { encoding: 'utf8' })
93
- assert.equal(ok.status, 0, ok.stderr)
94
- assert.equal(JSON.parse(readFileSync(path, 'utf8')).entries[0].inDiff, false)
95
- })
96
-
97
- test('a P0/P1, a missing location, or an unknown kind is rejected', () => {
98
- const box = emptyDropbox()
99
- assert.throws(() => addEntry(box, entry({ severity: 'P1' })), /never goes in the dropbox/)
100
- assert.throws(() => addEntry(box, entry({ file: undefined })), /--file is required/)
101
- assert.throws(() => addEntry(box, entry({ kind: 'nit' })), /--kind must be/)
102
- })
103
-
104
- test('the same problem is one entry, and a review finding id is one entry across rounds', () => {
105
- const box = emptyDropbox()
106
- const first = addEntry(box, entry())
107
- assert.deepEqual(addEntry(box, entry({ summary: ' Typo in heading ' })), { id: first.id, duplicate: true, inDiff: null })
108
- const r = addEntry(box, entry({ finding: 'R3', file: 'x.ts', summary: 'one wording' }))
109
- assert.equal(addEntry(box, entry({ finding: 'R3', file: 'x.ts', line: 9, summary: 'reworded next round' })).id, r.id)
110
- assert.equal(box.entries.length, 2)
111
- })
112
-
113
- test('local absolute paths never reach an issue, and summaries are capped', () => {
114
- assert.equal(sanitize('see /Users/someone/run/a.log and /tmp/x'), 'see <local path> and <local path>')
115
- assert.equal(sanitize('see src/tmp/cache.ts and app/home/page.tsx'), 'see src/tmp/cache.ts and app/home/page.tsx', 'repository paths stay')
116
- assert.ok(sanitize('x'.repeat(400)).length <= 280)
117
- })
118
-
119
- test('entries group by parent PR and by file and kind across PRs', () => {
120
- const box = emptyDropbox()
121
- addEntry(box, entry({ file: 'a.ts', kind: 'test', summary: '1', pr: 'https://github.com/o/r/pull/5' }))
122
- addEntry(box, entry({ file: 'b.ts', kind: 'docs', summary: '2', pr: 'https://github.com/o/r/pull/5' }))
123
- addEntry(box, entry({ file: 'a.ts', kind: 'test', summary: '3', pr: 'https://github.com/o/r/pull/6' }))
124
- addEntry(box, entry({ file: 'z.ts', kind: 'ci', summary: '4', pr: 'https://github.com/o/r/pull/7' }))
125
- const groups = groupEntries(box.entries)
126
- assert.deepEqual(groups.map((g) => [g.id, g.entries.map((e) => e.summary)]), [['G1', ['1', '2', '3']], ['G4', ['4']]])
127
- })
128
-
129
- test('the issue lists each location, its parent PR, what it blocks, and a fingerprint marker', () => {
130
- const box = emptyDropbox()
131
- addEntry(box, entry({ file: 'a.ts', line: 3, kind: 'test', summary: 'weak assert', pr: 'https://github.com/o/r/pull/5', finding: 'R2', blocks: ['https://github.com/o/r/issues/44'] }))
132
- addEntry(box, entry({ file: 'a.ts', line: 9, kind: 'test', summary: 'no failure case', pr: 'https://github.com/o/r/pull/5' }))
133
- const { title, body } = issueFor(groupEntries(box.entries)[0])
134
- assert.equal(title, '2 test follow-ups in a.ts')
135
- assert.match(body, /`a\.ts:3` \(test\) weak assert \(from #5, finding R2\)/)
136
- assert.match(body, /Blocks o\/r#44/)
137
- for (const e of box.entries) assert.ok(body.includes(`dropbox-fp:${e.fingerprint}`))
138
- })
139
-
140
- test('plan drops an entry whose file is gone and never posts decisions', () => {
141
- const dir = unitRepo()
142
- const box = emptyDropbox()
143
- addEntry(box, entry({ file: 'a.ts', summary: 'kept' }))
144
- addEntry(box, entry({ file: 'gone.ts', summary: 'gone' }))
145
- addEntry(box, { unit: '12', source: 'implementer', decision: true, summary: 'should refunds round half-even?' })
146
- const plan = planPost(box, { dir, ref: 'unit' })
147
- assert.deepEqual(plan.dropped, ['D2'])
148
- assert.deepEqual(plan.groups.flatMap((g) => g.entries.map((e) => e.summary)), ['kept'])
149
- assert.deepEqual(plan.decisions.map((d) => d.summary), ['should refunds round half-even?'])
150
- })
151
-
152
- function fakeGh(openIssues = []) {
153
- const calls = []
154
- return {
155
- calls,
156
- gh: (args, input) => {
157
- calls.push({ args, input })
158
- if (args[1] === 'list') return JSON.stringify(openIssues)
159
- if (args[1] === 'create') return `https://github.com/o/r/issues/${100 + calls.length}\n`
160
- throw new Error(`unexpected gh ${args.join(' ')}`)
161
- },
162
- }
163
- }
164
-
165
- test('post files one labeled issue per group and records each entry\'s URL', () => {
166
- const box = emptyDropbox()
167
- addEntry(box, entry({ file: 'a.ts', summary: '1', pr: 'https://github.com/o/r/pull/5' }))
168
- addEntry(box, entry({ file: 'b.ts', summary: '2', pr: 'https://github.com/o/r/pull/5' }))
169
- addEntry(box, entry({ file: 'c.ts', kind: 'ci', summary: '3' }))
170
- const { gh, calls } = fakeGh()
171
- const result = post(box, { repo: 'o/r' }, { gh })
172
- const creates = calls.filter((c) => c.args[1] === 'create')
173
- assert.equal(creates.length, 2)
174
- for (const c of creates) assert.deepEqual(c.args.slice(c.args.indexOf('--label'), c.args.indexOf('--label') + 2), ['--label', 'ready-for-agent'])
175
- assert.equal(result.posted.length, 2)
176
- assert.ok(box.entries.every((e) => e.status === 'posted' && e.postedUrl.startsWith('https://github.com/o/r/issues/')))
177
- })
178
-
179
- test('post skips an entry an open issue already carries, and --only limits the groups', () => {
180
- const box = emptyDropbox()
181
- addEntry(box, entry({ file: 'a.ts', summary: '1' }))
182
- addEntry(box, entry({ file: 'c.ts', kind: 'ci', summary: '3' }))
183
- addEntry(box, entry({ file: 'd.ts', kind: 'perf', summary: '4' }))
184
- const filed = { url: 'https://github.com/o/r/issues/9', body: `<!-- dropbox-fp:${box.entries[0].fingerprint} -->` }
185
- const { gh, calls } = fakeGh([filed])
186
- const result = post(box, { repo: 'o/r', only: ['G2'] }, { gh })
187
- assert.deepEqual(result.alreadyFiled, ['D1'])
188
- assert.equal(box.entries[0].postedUrl, filed.url)
189
- assert.equal(calls.filter((c) => c.args[1] === 'create').length, 1)
190
- assert.equal(box.entries[1].status, 'posted')
191
- assert.equal(box.entries[2].status, 'pending', 'G3 was not asked for')
192
- })
193
-
194
- test('from-review adds each undisposed P2 and writes its dropboxId back', () => {
195
- const box = emptyDropbox()
196
- const review = { unit: '12', findings: [
197
- { id: 'R1', severity: 'P1', summary: 'blocker', file: 'a.ts', line: 1 },
198
- { id: 'R2', severity: 'P2', pass: 'tests', summary: 'weak test', file: 'a.ts', line: 3 },
199
- { id: 'R3', severity: 'P2', summary: 'filed already', file: 'a.ts', followUpUrl: 'https://github.com/o/r/issues/1' },
200
- ] }
201
- const added = fromReview(box, review, { pr: 'https://github.com/o/r/pull/5', inDiffOf: (file, line) => line === 3 })
202
- assert.deepEqual(added, ['D1'])
203
- assert.equal(review.findings[1].dropboxId, 'D1')
204
- assert.equal(review.findings[0].dropboxId, undefined)
205
- assert.deepEqual([box.entries[0].kind, box.entries[0].inDiff, box.entries[0].finding], ['test', true, 'R2'])
206
- assert.deepEqual(fromReview(box, review), [], 'a second run adds nothing')
207
- })
208
-
209
- test('pending lists runs with unposted entries and exits 0 either way', () => {
210
- const runs = tmp()
211
- mkdirSync(join(runs, 'r1'))
212
- mkdirSync(join(runs, 'r2'))
213
- const box = emptyDropbox()
214
- addEntry(box, entry())
215
- writeFileSync(join(runs, 'r1', 'dropbox.json'), JSON.stringify(box))
216
- writeFileSync(join(runs, 'r2', 'dropbox.json'), JSON.stringify(emptyDropbox()))
217
- assert.deepEqual(pendingRuns(runs).map((r) => [r.run, r.pending]), [['r1', 1]])
218
- const cli = spawnSync('node', [script, 'pending', '--runs', runs], { encoding: 'utf8' })
219
- assert.equal(cli.status, 0, cli.stderr)
220
- assert.equal(JSON.parse(cli.stdout).runs.length, 1)
221
- const none = spawnSync('node', [script, 'pending', '--runs', join(runs, 'none')], { encoding: 'utf8' })
222
- assert.equal(none.status, 0, none.stderr)
223
- assert.deepEqual(JSON.parse(none.stdout).runs, [])
224
- })
225
-
226
- test('a problem found again at a shifted line is the same problem', () => {
227
- assert.equal(fingerprint(entry({ line: 4 })), fingerprint(entry({ line: 40 })))
228
- })
229
-
230
- test('the diff can be taken at a given head, whatever the checkout', () => {
231
- const dir = unitRepo()
232
- const head = execFileSync('git', ['-C', dir, 'rev-parse', 'unit'], { encoding: 'utf8' }).trim()
233
- execFileSync('git', ['-C', dir, 'checkout', '-q', 'main'])
234
- assert.equal(inUnitDiff({ dir, base: 'main', file: 'a.ts', line: 3 }), false, 'the checkout is main')
235
- assert.equal(inUnitDiff({ dir, base: 'main', head, file: 'a.ts', line: 3 }), true)
236
- })
237
-
238
- test('from-review takes the unit diff at the review head', () => {
239
- const box = emptyDropbox()
240
- const seen = []
241
- fromReview(box, { unit: '1', head: 'abc', findings: [{ id: 'R2', severity: 'P2', summary: 's', file: 'a.ts', line: 3 }] }, { inDiffOf: (...args) => { seen.push(args); return false } })
242
- assert.deepEqual(seen, [['a.ts', 3, 'abc']])
243
- })
244
-
245
- test('an urgent entry is filed alone with --ids, and is not filed again at the end', () => {
246
- const box = emptyDropbox()
247
- addEntry(box, entry({ file: 'a.ts', summary: 'nit', pr: 'https://github.com/o/r/pull/5' }))
248
- addEntry(box, entry({ file: 'b.ts', kind: 'bug', summary: 'cross-org role change', pr: 'https://github.com/o/r/pull/5' }))
249
- const first = fakeGh()
250
- post(box, { repo: 'o/r', ids: ['D2'] }, { gh: first.gh })
251
- const created = first.calls.filter((c) => c.args[1] === 'create')
252
- assert.equal(created.length, 1)
253
- assert.match(created[0].input, /cross-org role change/)
254
- assert.doesNotMatch(created[0].input, /nit/, 'its PR group-mate stays for the end')
255
- const end = fakeGh()
256
- post(box, { repo: 'o/r' }, { gh: end.gh })
257
- const bodies = end.calls.filter((c) => c.args[1] === 'create').map((c) => c.input)
258
- assert.equal(bodies.length, 1)
259
- assert.doesNotMatch(bodies[0], /cross-org/)
260
- assert.throws(() => post(box, { repo: 'o/r', ids: ['D9'] }, { gh: end.gh }), /no entry D9/)
261
- })
262
-
263
- test('post finds already-filed entries among open issues carrying its label', () => {
264
- const { gh, calls } = fakeGh()
265
- post(emptyDropbox(), { repo: 'o/r', label: 'agent-ready' }, { gh })
266
- const list = calls.find((c) => c.args[1] === 'list').args
267
- assert.deepEqual(list.slice(list.indexOf('--label'), list.indexOf('--label') + 2), ['--label', 'agent-ready'])
268
- })
269
-
270
- test('mark drops an entry or turns it into a decision; a posted entry cannot change', () => {
271
- const box = emptyDropbox()
272
- addEntry(box, entry({ file: 'a.ts', summary: '1' }))
273
- addEntry(box, entry({ file: 'b.ts', summary: '2' }))
274
- markEntry(box, { id: 'D1', drop: 'file removed by epic #40' })
275
- markEntry(box, { id: 'D2', decision: true })
276
- assert.deepEqual(box.entries.map((e) => [e.status, e.route]), [['dropped', 'dropbox'], ['pending', 'decision']])
277
- assert.deepEqual(planPost(box).groups, [])
278
- assert.throws(() => markEntry(box, { id: 'D1' }), /--drop <reason> or --decision/)
279
- box.entries[1].status = 'posted'
280
- assert.throws(() => markEntry(box, { id: 'D2', drop: 'x' }), /already posted/)
281
- })
282
-
283
- test('pending ignores decisions, which their own run reports', () => {
284
- const runs = tmp()
285
- mkdirSync(join(runs, 'r1'))
286
- const box = emptyDropbox()
287
- addEntry(box, { unit: '1', source: 'implementer', decision: true, summary: 'which period?' })
288
- writeFileSync(join(runs, 'r1', 'dropbox.json'), JSON.stringify(box))
289
- assert.deepEqual(pendingRuns(runs), [])
290
- })
291
-
292
- test('a file the unit deleted is inside its diff; a path that never existed is an error', () => {
293
- const dir = unitRepo()
294
- execFileSync('git', ['-C', dir, 'rm', '-q', 'a.ts'])
295
- execFileSync('git', ['-C', dir, 'commit', '-qm', 'drop a'])
296
- assert.equal(inUnitDiff({ dir, base: 'main', file: 'a.ts', line: 7 }), true)
297
- assert.throws(() => inUnitDiff({ dir, base: 'main', file: 'nope.ts', line: 1 }), /exists neither/)
298
- assert.throws(() => inUnitDiff({ dir, base: 'main', file: 'b.ts', line: Number('abc') }), /positive integer/)
299
- })
300
-
301
- test('check and add refuse bad input instead of calling it outside the diff', () => {
302
- const dir = unitRepo()
303
- const run = (...args) => spawnSync('node', [script, ...args], { encoding: 'utf8' })
304
- assert.equal(run('check', '--git', dir, '--base', 'main', '--file', 'a.ts', '--line', 'abc').status, 2)
305
- assert.equal(run('check', '--git', dir, '--base', 'main', '--file', 'a.ts:5').status, 2)
306
- assert.equal(run('check', '--git', dir, '--base', 'main', '--file', 'a.tss', '--line', '1').status, 2)
307
- const path = join(tmp(), 'dropbox.json')
308
- const noDiff = run('add', '--dropbox', path, '--unit', '7', '--source', 'implementer', '--kind', 'bug', '--file', 'a.ts', '--line', '3', '--summary', 's')
309
- assert.equal(noDiff.status, 2, 'an implementer entry needs the diff check')
310
- assert.match(noDiff.stderr, /--git, --base/)
311
- assert.throws(() => addEntry(emptyDropbox(), entry({ file: join(dir, 'a.ts') })), /repository-relative/)
312
- })
313
-
314
- test('a bare issue number in --blocks becomes a link', () => {
315
- const box = emptyDropbox()
316
- addEntry(box, entry({ blocks: ['55'] }))
317
- assert.match(issueFor(groupEntries(box.entries)[0]).body, /Blocks #55/)
318
- })
319
-
320
- test('post drops entries whose file is gone at --ref and posts --only groups by plan id', () => {
321
- const dir = unitRepo()
322
- const path = join(tmp(), 'dropbox.json')
323
- const box = emptyDropbox()
324
- addEntry(box, entry({ file: 'gone.ts', summary: 'gone' }))
325
- addEntry(box, entry({ file: 'a.ts', kind: 'test', summary: 'kept 1' }))
326
- addEntry(box, entry({ file: 'b.ts', kind: 'ci', summary: 'kept 2' }))
327
- writeFileSync(path, JSON.stringify(box))
328
- const bin = tmp()
329
- const log = join(bin, 'log')
330
- writeFileSync(join(bin, 'gh'), `#!/bin/sh\necho "$@" >> ${log}\ncat >> ${log}\ncase "$2" in list) echo '[]';; create) echo https://github.com/o/r/issues/101;; esac\n`, { mode: 0o755 })
331
- const env = { ...process.env, PATH: `${bin}:${process.env.PATH}` }
332
- const planned = JSON.parse(spawnSync('node', [script, 'plan', '--dropbox', path, '--git', dir, '--ref', 'unit'], { encoding: 'utf8' }).stdout)
333
- assert.deepEqual(planned.groups.map((g) => g.id), ['G2', 'G3'])
334
- const result = spawnSync('node', [script, 'post', '--dropbox', path, '--repo', 'o/r', '--git', dir, '--ref', 'unit', '--only', 'G3'], { encoding: 'utf8', env })
335
- assert.equal(result.status, 0, result.stderr)
336
- const after = JSON.parse(readFileSync(path, 'utf8')).entries
337
- assert.deepEqual(after.map((e) => e.status), ['dropped', 'pending', 'posted'])
338
- assert.doesNotMatch(readFileSync(log, 'utf8'), /gone/)
339
- assert.equal(spawnSync('node', [script, 'plan', '--dropbox', path, '--ref', 'unit'], { encoding: 'utf8' }).status, 2, '--ref needs --git')
340
- })
341
-
342
- test('post --ids names a decision as one', () => {
343
- const box = emptyDropbox()
344
- addEntry(box, { unit: '1', source: 'implementer', decision: true, summary: 'which period?' })
345
- assert.throws(() => post(box, { repo: 'o/r', ids: ['D1'] }, { gh: fakeGh().gh }), /D1 is a decision/)
346
- })
@@ -1,88 +0,0 @@
1
- import { test } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import { execFileSync, spawnSync } from 'node:child_process'
4
- import fs from 'node:fs'
5
- import os from 'node:os'
6
- import path from 'node:path'
7
- import { fileURLToPath } from 'node:url'
8
-
9
- import { pathsIn, evidencePaths, decideReuse, parseCliArgs, FindingsReuseError } from './findings-reuse.mjs'
10
-
11
- const SCRIPT = path.join(path.dirname(fileURLToPath(import.meta.url)), 'findings-reuse.mjs')
12
-
13
- // A repo at `base`; then `src/charge/layout.ts` changes on main.
14
- function fixture(t) {
15
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'findings-reuse-'))
16
- t.after(() => fs.rmSync(dir, { recursive: true, force: true }))
17
- const git = (...a) => execFileSync('git', a, { cwd: dir, encoding: 'utf8' }).trim()
18
- const write = (f, text) => { fs.mkdirSync(path.dirname(path.join(dir, f)), { recursive: true }); fs.writeFileSync(path.join(dir, f), text) }
19
- git('init', '-q', '-b', 'main'); git('config', 'user.email', 't@e.com'); git('config', 'user.name', 'T')
20
- write('src/charge/layout.ts', 'a\n'); write('src/history/list.ts', 'b\n'); write('db/schema.sql', 'c\n')
21
- git('add', '-A'); git('commit', '-qm', 'base')
22
- const base = git('rev-parse', 'HEAD')
23
- write('src/charge/layout.ts', 'a2\n'); git('commit', '-qam', 'earlier units merged')
24
- const prior = {
25
- version: 2, base,
26
- units: {
27
- charge: { slice: { prerequisites: [{ what: 'layout store', status: 'present', evidence: 'src/charge/layout.ts:3' }] }, readFirst: [] },
28
- history: { slice: { prerequisites: [{ what: 'list view', status: 'present', evidence: 'src/history/list.ts:1' }] }, readFirst: ['src/history/'] },
29
- migrate: { slice: { prerequisites: [{ what: 'cursor column', status: 'missing', separate: false, evidence: 'not in db/schema.sql' }] } },
30
- },
31
- }
32
- const priorFile = path.join(dir, '..', `${path.basename(dir)}-prior.json`)
33
- fs.writeFileSync(priorFile, JSON.stringify(prior))
34
- t.after(() => fs.rmSync(priorFile, { force: true }))
35
- const run = (...args) => spawnSync('node', [SCRIPT, '--prior', priorFile, ...args], { cwd: dir, encoding: 'utf8' })
36
- return { dir, git, write, base, prior, run }
37
- }
38
-
39
- test('pathsIn finds repository paths in evidence prose, without line numbers', () => {
40
- assert.deepEqual(pathsIn('packages/core/page.ts:12'), ['packages/core/page.ts'])
41
- assert.deepEqual(pathsIn('not in db/schema.sql'), ['db/schema.sql'])
42
- assert.deepEqual(pathsIn('no authority in backend/services'), ['backend/services'])
43
- assert.deepEqual(pathsIn('e2e job green'), [])
44
- })
45
-
46
- test('evidencePaths reads prerequisites, readFirst (or legacy start), and the suite', () => {
47
- const entry = {
48
- slice: { prerequisites: [{ evidence: 'a/b.ts:1' }] },
49
- start: ['c/d.ts'],
50
- proof: { suite: { evidence: 'scripts/journey.py; green in CI', check: 'gh workflow run android.yml', local: 'bash scripts/charge-journey.sh' } },
51
- }
52
- // A renamed local journey script invalidates the reuse, like any other evidence path.
53
- assert.deepEqual(evidencePaths(entry), ['a/b.ts', 'android.yml', 'c/d.ts', 'scripts/charge-journey.sh', 'scripts/journey.py'])
54
- })
55
-
56
- test('a unit is reused unless a file its evidence names changed; directories match files under them', () => {
57
- const prior = { units: { a: { slice: { prerequisites: [{ evidence: 'src/x.ts:1' }] } }, b: { readFirst: ['src/y/'] } } }
58
- assert.deepEqual(decideReuse(prior, ['a', 'b', 'c'], ['src/y/z.ts'], true), { reuse: ['a'], recheck: [{ id: 'b', changed: ['src/y/z.ts'] }], new: ['c'] })
59
- assert.deepEqual(decideReuse(prior, ['a'], [], false).recheck.map((r) => r.id), ['a'], 'unknown base rechecks everything')
60
- })
61
-
62
- test('CLI: after an earlier unit changed the charge layout, only the charge unit is rechecked', (t) => {
63
- const f = fixture(t)
64
- const out = JSON.parse(f.run().stdout)
65
- assert.deepEqual(out.reuse, ['history', 'migrate'])
66
- assert.deepEqual(out.recheck, [{ id: 'charge', changed: ['src/charge/layout.ts'] }])
67
- assert.deepEqual(out.new, [])
68
- const only = JSON.parse(f.run('--units', 'history,newunit').stdout)
69
- assert.deepEqual(only.reuse, ['history'])
70
- assert.deepEqual(only.new, ['newunit'])
71
- })
72
-
73
- test('CLI: a missing prerequisite whose evidence file changed is rechecked (it may now exist)', (t) => {
74
- const f = fixture(t)
75
- f.write('db/schema.sql', 'c\ncursor\n'); f.git('commit', '-qam', 'add cursor')
76
- assert.deepEqual(JSON.parse(f.run().stdout).recheck.map((r) => r.id), ['charge', 'migrate'])
77
- })
78
-
79
- test('CLI: a prior base not in this repository rechecks every unit; bad input exits 2', (t) => {
80
- const f = fixture(t)
81
- const priorFile = path.join(f.dir, 'gone.json')
82
- fs.writeFileSync(priorFile, JSON.stringify({ ...f.prior, base: '0'.repeat(40) }))
83
- const out = JSON.parse(spawnSync('node', [SCRIPT, '--prior', priorFile], { cwd: f.dir, encoding: 'utf8' }).stdout)
84
- assert.deepEqual(out.reuse, [])
85
- assert.equal(out.recheck.length, 3)
86
- assert.equal(spawnSync('node', [SCRIPT, '--prior', path.join(f.dir, 'missing.json')], { cwd: f.dir }).status, 2)
87
- assert.throws(() => parseCliArgs([]), FindingsReuseError)
88
- })