@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,95 +0,0 @@
1
- import { test } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
4
- import { tmpdir } from 'node:os';
5
- import { join } from 'node:path';
6
-
7
- import {
8
- applyInvocationPolicy,
9
- clearInvocationPolicy,
10
- MANUAL_ONLY,
11
- manualOnlyPolicyFindings,
12
- reconcileInvocationPolicy,
13
- reviewedSourceInjections,
14
- } from './invocation-policy.mjs';
15
-
16
- const UPSTREAM_SKILL = `---
17
- name: tdd
18
- description: Test-driven development.
19
- ---
20
-
21
- # TDD
22
- `;
23
- const UPSTREAM_OPENAI = `interface:
24
- display_name: "TDD"
25
- short_description: "Test-driven red-green-refactor"
26
- `;
27
-
28
- function fixture() {
29
- const root = mkdtempSync(join(tmpdir(), 'invocation-policy-'));
30
- const skill = join(root, 'tdd');
31
- mkdirSync(join(skill, 'agents'), { recursive: true });
32
- writeFileSync(join(skill, 'SKILL.md'), UPSTREAM_SKILL);
33
- writeFileSync(join(skill, 'agents', 'openai.yaml'), UPSTREAM_OPENAI);
34
- return { root, skill };
35
- }
36
-
37
- test('manual-only policy is idempotent and preserves explicit invocation metadata', () => {
38
- const { root, skill } = fixture();
39
- const first = applyInvocationPolicy({ installName: 'tdd', policy: MANUAL_ONLY, skillsRoot: root });
40
- const skillOnce = readFileSync(join(skill, 'SKILL.md'), 'utf8');
41
- const openaiOnce = readFileSync(join(skill, 'agents', 'openai.yaml'), 'utf8');
42
- const second = applyInvocationPolicy({ installName: 'tdd', policy: MANUAL_ONLY, skillsRoot: root });
43
-
44
- assert.equal(first.changed, true);
45
- assert.equal(second.changed, false);
46
- assert.match(skillOnce, /^disable-model-invocation: true$/m);
47
- assert.match(openaiOnce, /^policy:\n allow_implicit_invocation: false$/m);
48
- assert.deepEqual(manualOnlyPolicyFindings(skill), []);
49
- assert.equal(readFileSync(join(skill, 'SKILL.md'), 'utf8'), skillOnce);
50
- assert.equal(readFileSync(join(skill, 'agents', 'openai.yaml'), 'utf8'), openaiOnce);
51
- });
52
-
53
- test('reviewed source injections remove only the deterministic runtime policy overlay', () => {
54
- const { root, skill } = fixture();
55
- applyInvocationPolicy({ installName: 'tdd', policy: MANUAL_ONLY, skillsRoot: root });
56
- const injected = Object.fromEntries(reviewedSourceInjections(skill, MANUAL_ONLY)
57
- .map((entry) => [entry.relativePath, entry.contents]));
58
- assert.equal(injected['SKILL.md'], UPSTREAM_SKILL);
59
- assert.equal(injected['agents/openai.yaml'], UPSTREAM_OPENAI);
60
-
61
- const cleared = clearInvocationPolicy({ installName: 'tdd', policy: MANUAL_ONLY, skillsRoot: root });
62
- assert.equal(cleared.changed, true);
63
- assert.equal(readFileSync(join(skill, 'SKILL.md'), 'utf8'), UPSTREAM_SKILL);
64
- assert.equal(readFileSync(join(skill, 'agents', 'openai.yaml'), 'utf8'), UPSTREAM_OPENAI);
65
- assert.equal(clearInvocationPolicy({ installName: 'tdd', policy: MANUAL_ONLY, skillsRoot: root }).changed, false);
66
- });
67
-
68
- test('a missing selection defaults TDD to implicit invocation', () => {
69
- const { root, skill } = fixture();
70
- applyInvocationPolicy({ installName: 'tdd', policy: MANUAL_ONLY, skillsRoot: root });
71
- const home = mkdtempSync(join(tmpdir(), 'invocation-policy-home-'));
72
- const result = reconcileInvocationPolicy({
73
- installName: 'tdd',
74
- policy: MANUAL_ONLY,
75
- skillsRoot: root,
76
- env: { HOME: home },
77
- });
78
- assert.equal(result.policy, 'implicit');
79
- assert.equal(result.changed, true);
80
- assert.equal(readFileSync(join(skill, 'SKILL.md'), 'utf8'), UPSTREAM_SKILL);
81
- assert.equal(readFileSync(join(skill, 'agents', 'openai.yaml'), 'utf8'), UPSTREAM_OPENAI);
82
- });
83
-
84
- test('policy application fails closed on malformed or missing skill metadata', () => {
85
- const { root, skill } = fixture();
86
- writeFileSync(join(skill, 'SKILL.md'), 'not frontmatter\n');
87
- assert.throws(
88
- () => applyInvocationPolicy({ installName: 'tdd', policy: MANUAL_ONLY, skillsRoot: root }),
89
- /must begin with YAML frontmatter/,
90
- );
91
- assert.throws(
92
- () => applyInvocationPolicy({ installName: '../tdd', policy: MANUAL_ONLY, skillsRoot: root }),
93
- /invalid installed skill name/,
94
- );
95
- });
@@ -1,288 +0,0 @@
1
- // Behaviour gate for the shared ESM main-module guard (issues #718 and #446).
2
- //
3
- // The two failure modes this pins are opposite halves of one idiom, and both are silent:
4
- //
5
- // #718 — comparing `import.meta.url` against an un-realpathed `process.argv[1]`. Node resolves
6
- // `import.meta.url` to the REAL file; `argv[1]` keeps whatever the caller typed. Invoked
7
- // through a symlink — which is exactly how every skills install works — the two never
8
- // match, the CLI never runs, and node exits 0 having done nothing. On a gate whose job is
9
- // to say *no*, "passed" and "never ran" produce byte-identical evidence.
10
- // #446 — calling `fs.realpathSync(process.argv[1])` unguarded. `node -` sets `argv[1]` to the
11
- // literal string `"-"`, which is truthy and is not a file, so realpath throws ENOENT at
12
- // MODULE LOAD — before any of the module's own code runs. The module becomes unimportable
13
- // by any consumer that does not launch it as a file.
14
- //
15
- // So the in-process predicate tests below are necessary but not sufficient: #446 is a *load-time*
16
- // throw, which an in-process test cannot observe at all. Every claim that matters here is also
17
- // driven through a real child process — a real temp symlink, a real `node -` on stdin, and the
18
- // real `coverage-ledger.mjs` that #718 was filed against.
19
- //
20
- // Discovered and run by .github/workflows/ci.yml (every skills/**/*.test.mjs).
21
- import { test } from 'node:test';
22
- import assert from 'node:assert/strict';
23
- import { execFileSync, spawnSync } from 'node:child_process';
24
- import { cpSync, mkdtempSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
25
- import { tmpdir } from 'node:os';
26
- import { dirname, join } from 'node:path';
27
- import { fileURLToPath, pathToFileURL } from 'node:url';
28
-
29
- import { isMainModule } from './main-module.mjs';
30
-
31
- const HERE = dirname(fileURLToPath(import.meta.url)); // skills/engineering/engineering-runtime/scripts
32
- const SKILLS_DIR = dirname(dirname(dirname(HERE))); // skills/
33
- const REPO = dirname(SKILLS_DIR);
34
- const HELPER_URL = pathToFileURL(join(HERE, 'main-module.mjs')).href;
35
-
36
- // Every child-process case builds its own scratch dir and removes it, so a failing run leaves
37
- // nothing behind and two runs never share a symlink.
38
- function withTmp(fn) {
39
- const dir = mkdtempSync(join(tmpdir(), 'main-module-'));
40
- try {
41
- return fn(dir);
42
- } finally {
43
- rmSync(dir, { recursive: true, force: true });
44
- }
45
- }
46
-
47
- // A minimal CLI with the exact shape every converted site has: import the helper, run the CLI only
48
- // when the guard says this module is main, and exit with a distinctive code so "ran" and "did not
49
- // run" cannot be confused with each other.
50
- const FIXTURE_CLI = `
51
- import { isMainModule } from ${JSON.stringify(HELPER_URL)};
52
- export const marker = 'imported-cleanly';
53
- if (isMainModule(import.meta.url)) {
54
- console.log('CLI RAN');
55
- process.exit(7);
56
- }
57
- `;
58
-
59
- function runNode(args, opts = {}) {
60
- const r = spawnSync(process.execPath, args, { encoding: 'utf8', ...opts });
61
- return { status: r.status, stdout: r.stdout ?? '', stderr: r.stderr ?? '' };
62
- }
63
-
64
- // --- the predicate itself -------------------------------------------------------------------
65
- // Restoring argv[1] rather than assigning a saved copy back blindly: `node --test` needs it, and a
66
- // test that corrupted it would poison every later file in the same process.
67
-
68
- function withArgv1(value, fn) {
69
- const saved = process.argv[1];
70
- if (value === undefined) delete process.argv[1];
71
- else process.argv[1] = value;
72
- try {
73
- return fn();
74
- } finally {
75
- process.argv[1] = saved;
76
- }
77
- }
78
-
79
- test('argv[1] absent → not main, and no throw (#446)', () => {
80
- withArgv1(undefined, () => {
81
- assert.equal(isMainModule(import.meta.url), false);
82
- });
83
- });
84
-
85
- test('argv[1] set to a path that is not a real file → not main, and no throw (#446)', () => {
86
- // `"-"` is not a hypothetical: it is literally what `node -` puts in argv[1].
87
- for (const value of ['-', '', '/definitely/not/a/real/path.mjs', '[eval]']) {
88
- withArgv1(value, () => {
89
- assert.equal(isMainModule(import.meta.url), false, `argv[1]=${JSON.stringify(value)}`);
90
- });
91
- }
92
- });
93
-
94
- test('argv[1] is this module → main (AC7: the true branch still fires)', () => {
95
- const self = fileURLToPath(import.meta.url);
96
- withArgv1(self, () => {
97
- assert.equal(isMainModule(import.meta.url), true);
98
- });
99
- });
100
-
101
- test('argv[1] is a SYMLINK to this module → still main (#718)', () => {
102
- withTmp((dir) => {
103
- const link = join(dir, 'aliased-name.mjs');
104
- symlinkSync(fileURLToPath(import.meta.url), link);
105
- withArgv1(link, () => {
106
- assert.equal(isMainModule(import.meta.url), true, 'a symlinked invocation is the main module');
107
- });
108
- // The pre-fix predicate is what this must not degrade back into. Stated as an executable
109
- // contrast so the assertion above cannot be satisfied by an implementation that happens to
110
- // compare raw strings on a machine where nothing is symlinked.
111
- assert.notEqual(pathToFileURL(link).href, import.meta.url, 'the naive comparison would be false here');
112
- });
113
- });
114
-
115
- test('argv[1] is a different real file → not main', () => {
116
- withArgv1(join(REPO, 'CLAUDE.md'), () => {
117
- assert.equal(isMainModule(import.meta.url), false);
118
- });
119
- });
120
-
121
- test('a non-file import.meta.url → not main, and no throw', () => {
122
- withArgv1(fileURLToPath(import.meta.url), () => {
123
- assert.equal(isMainModule('data:text/javascript,export{}'), false);
124
- });
125
- });
126
-
127
- // --- real child processes -------------------------------------------------------------------
128
-
129
- test('a CLI invoked through a symlink runs, byte-identically to a direct invocation (#718)', () => {
130
- withTmp((dir) => {
131
- const real = join(dir, 'real-cli.mjs');
132
- const link = join(dir, 'installed-cli.mjs');
133
- writeFileSync(real, FIXTURE_CLI);
134
- symlinkSync(real, link);
135
-
136
- const direct = runNode([real]);
137
- const viaLink = runNode([link]);
138
-
139
- assert.equal(direct.stdout.trim(), 'CLI RAN');
140
- assert.equal(direct.status, 7);
141
- assert.deepEqual(
142
- { stdout: viaLink.stdout, status: viaLink.status },
143
- { stdout: direct.stdout, status: direct.status },
144
- `the symlinked invocation must produce the same output and exit code.\nstderr:\n${viaLink.stderr}`,
145
- );
146
- });
147
- });
148
-
149
- test('a directory symlink — the actual skills-install shape — resolves too (#718)', () => {
150
- withTmp((dir) => {
151
- // ~/.claude/skills/<skill> is a symlink to the skill DIRECTORY, not to each script, so the
152
- // symlink sits in the middle of the path rather than at its end.
153
- const skill = join(dir, 'real-skill');
154
- mkdirSync(join(skill, 'scripts'), { recursive: true });
155
- const script = join(skill, 'scripts', 'cli.mjs');
156
- writeFileSync(script, FIXTURE_CLI);
157
- const installed = join(dir, 'installed-skill');
158
- symlinkSync(skill, installed);
159
-
160
- const viaDir = runNode([join(installed, 'scripts', 'cli.mjs')]);
161
- assert.equal(viaDir.stdout.trim(), 'CLI RAN', `stderr:\n${viaDir.stderr}`);
162
- assert.equal(viaDir.status, 7);
163
- });
164
- });
165
-
166
- test('`node -` can import a module that uses the guard, without throwing at load (#446, AC8)', () => {
167
- withTmp((dir) => {
168
- const mod = join(dir, 'cli.mjs');
169
- writeFileSync(mod, FIXTURE_CLI);
170
- // Reading the program from stdin is the case that sets argv[1] to the literal "-". This is the
171
- // invocation that threw ENOENT at module load before the fix, so it is run as a real child
172
- // process — an in-process test cannot observe a load-time throw.
173
- const r = runNode(['-'], {
174
- input: `const m = await import(${JSON.stringify(pathToFileURL(mod).href)});\nconsole.log(m.marker);\n`,
175
- });
176
- assert.equal(r.status, 0, `import under \`node -\` must not throw.\nstderr:\n${r.stderr}`);
177
- assert.equal(r.stdout.trim(), 'imported-cleanly');
178
- assert.ok(!r.stdout.includes('CLI RAN'), 'an imported module must not execute its CLI');
179
- });
180
- });
181
-
182
- test('argv[1] set to a nonexistent path does not stop a module from loading (#446)', () => {
183
- withTmp((dir) => {
184
- const mod = join(dir, 'cli.mjs');
185
- writeFileSync(mod, FIXTURE_CLI);
186
- const driver = join(dir, 'driver.mjs');
187
- writeFileSync(
188
- driver,
189
- `process.argv[1] = '/no/such/file.mjs';\n` +
190
- `const m = await import(${JSON.stringify(pathToFileURL(mod).href)});\n` +
191
- `console.log(m.marker);\n`,
192
- );
193
- const r = runNode([driver]);
194
- assert.equal(r.status, 0, `stderr:\n${r.stderr}`);
195
- assert.equal(r.stdout.trim(), 'imported-cleanly');
196
- });
197
- });
198
-
199
- // --- the real script #718 was filed against ---------------------------------------------------
200
-
201
- test('coverage-ledger.mjs: symlinked and direct invocations agree (#718 AC1)', () => {
202
- withTmp((dir) => {
203
- // A scratch cwd with no surface inventory, so the honest answer is a deterministic failure —
204
- // exit 2 — rather than a pass. That is the whole point of the issue: before the fix the
205
- // symlinked run printed nothing and exited 0, which is indistinguishable from success.
206
- const cwd = join(dir, 'workdir');
207
- mkdirSync(cwd);
208
- const skill = join(SKILLS_DIR, 'engineering', 'ensure-coverage');
209
- const installed = join(dir, 'ensure-coverage');
210
- symlinkSync(skill, installed);
211
-
212
- const argsFor = (root) => [join(root, 'scripts', 'coverage-ledger.mjs'), 'validate'];
213
- const direct = runNode(argsFor(skill), { cwd });
214
- const viaLink = runNode(argsFor(installed), { cwd });
215
-
216
- assert.equal(direct.status, 2, `expected the honest failure, got:\n${direct.stdout}${direct.stderr}`);
217
- assert.match(direct.stderr, /Inventory not found/);
218
- assert.deepEqual(
219
- { status: viaLink.status, stdout: viaLink.stdout, stderr: viaLink.stderr },
220
- { status: direct.status, stdout: direct.stdout, stderr: direct.stderr },
221
- 'a symlinked coverage-ledger must not silently no-op and exit 0',
222
- );
223
- });
224
- });
225
-
226
- test('flat installation and Claude skill symlink preserve direct launch and import behavior', () => {
227
- withTmp((dir) => {
228
- const canonical = join(dir, '.agents', 'skills');
229
- const claude = join(dir, '.claude', 'skills');
230
- const sourceEngineering = join(SKILLS_DIR, 'engineering');
231
- mkdirSync(canonical, { recursive: true });
232
- mkdirSync(claude, { recursive: true });
233
- cpSync(join(sourceEngineering, 'ensure-coverage'), join(canonical, 'ensure-coverage'), { recursive: true });
234
- cpSync(join(sourceEngineering, 'engineering-runtime'), join(canonical, 'engineering-runtime'), { recursive: true });
235
- symlinkSync(join(canonical, 'ensure-coverage'), join(claude, 'ensure-coverage'));
236
- symlinkSync(join(canonical, 'engineering-runtime'), join(claude, 'engineering-runtime'));
237
-
238
- const cwd = join(dir, 'workdir');
239
- mkdirSync(cwd);
240
- const directScript = join(canonical, 'ensure-coverage', 'scripts', 'coverage-ledger.mjs');
241
- const claudeScript = join(claude, 'ensure-coverage', 'scripts', 'coverage-ledger.mjs');
242
- const direct = runNode([directScript, 'validate'], { cwd });
243
- const viaClaude = runNode([claudeScript, 'validate'], { cwd });
244
-
245
- assert.equal(direct.status, 2, `the installed CLI must reach its honest failure:\n${direct.stderr}`);
246
- assert.match(direct.stderr, /Inventory not found/);
247
- assert.deepEqual(
248
- { status: viaClaude.status, stdout: viaClaude.stdout, stderr: viaClaude.stderr },
249
- { status: direct.status, stdout: direct.stdout, stderr: direct.stderr },
250
- 'Claude symlink launch must preserve installed CLI output and exit status',
251
- );
252
-
253
- const imported = runNode(['-'], {
254
- cwd,
255
- input: `await import(${JSON.stringify(pathToFileURL(directScript).href)});\nconsole.log('IMPORTED');\n`,
256
- });
257
- assert.equal(imported.status, 0, `a flat-installed Workflow module must import under non-file argv:\n${imported.stderr}`);
258
- assert.equal(imported.stdout.trim(), 'IMPORTED');
259
- });
260
- });
261
-
262
- // --- the whole converted class, not just the fixture -------------------------------------------
263
-
264
- test('every script that adopted the guard is importable with argv[1] = "-" (#446 AC6/AC8)', () => {
265
- // The class, executed. `grep` for the helper import, then import all of them in one child whose
266
- // argv[1] is the literal "-": before the fix this is precisely the invocation that threw ENOENT
267
- // at load, so a regression at ANY single site turns this red without anyone maintaining a list.
268
- const listed = execFileSync(
269
- 'git',
270
- ['grep', '-l', '--', 'main-module.mjs', ':(exclude)skills/engineering/engineering-runtime', 'skills'],
271
- { cwd: REPO, encoding: 'utf8' },
272
- )
273
- .split('\n')
274
- .filter((p) => p.endsWith('.mjs') && !p.endsWith('.test.mjs'));
275
-
276
- assert.ok(listed.length >= 30, `expected the converted class to be found, got ${listed.length}`);
277
-
278
- const program = listed
279
- .map((p) => `await import(${JSON.stringify(pathToFileURL(join(REPO, p)).href)});`)
280
- .join('\n');
281
- const r = runNode(['-'], { input: `${program}\nconsole.log('ALL IMPORTED');\n`, cwd: REPO });
282
- assert.equal(
283
- r.status,
284
- 0,
285
- `importing the converted scripts under \`node -\` must not throw.\nstderr:\n${r.stderr}`,
286
- );
287
- assert.match(r.stdout, /ALL IMPORTED/);
288
- });
@@ -1,96 +0,0 @@
1
- import assert from 'node:assert/strict'
2
- import test from 'node:test'
3
- import { locallyReproducedJobs, validateQueueExecutionArtifact, validateRequiredCheckExecution, validateRequiredCheckPlan } from './required-check-plan.mjs'
4
-
5
- const head = 'a'.repeat(40)
6
- const plan = () => ({
7
- version: 1,
8
- kind: 'required-check-plan',
9
- repository: '1aboveio/ai',
10
- targetBranch: 'main',
11
- headSha: head,
12
- discovery: {
13
- github: { conditions: ['required_status_checks: Mergify Merge Protections'], trees: [{ condition: 'required_status_checks: Mergify Merge Protections' }] },
14
- mergify: { conditions: ['check-success = skill-script-tests', 'check-success = skill-size'], trees: [{ all: [{ condition: 'check-success = skill-script-tests' }, { condition: 'check-success = skill-size' }] }] },
15
- requiredChecks: ['Mergify Merge Protections', 'skill-script-tests', 'skill-size'],
16
- },
17
- checks: [
18
- { name: 'Mergify Merge Protections', admission: 'required', classification: 'publish-only', sources: ['github-ruleset'], evidence: { provider: 'github', reason: 'Mergify publishes this aggregate condition after PR creation' } },
19
- { name: 'skill-script-tests', admission: 'required', classification: 'locally-reproduced', sources: ['mergify'], evidence: { command: 'node --test skills/**/*.test.mjs', job: 'skill-script-tests', result: 'success' } },
20
- { name: 'skill-size', admission: 'required', classification: 'locally-reproduced', sources: ['mergify'], evidence: { command: 'node --test skills/skill-size.test.mjs', job: 'skill-size', result: 'success' } },
21
- ],
22
- allowedSkippedJobs: ['skill-script-tests-queue'],
23
- })
24
-
25
- // @covers job:resolve-issues/exact-head-ci
26
- // @level integration
27
- // @fails-without-fix job:resolve-issues/exact-head-ci
28
- test('epic-892: a plan rejects omitted live skill-size despite otherwise green evidence', () => {
29
- const incomplete = plan()
30
- incomplete.checks.pop()
31
- const verdict = validateRequiredCheckPlan(incomplete, { expectedHeadSha: head, requireEvidence: true })
32
- assert.equal(verdict.ok, false)
33
- assert.match(verdict.errors.join(' '), /omits live required check skill-size.*epic-892/i)
34
- })
35
-
36
- // @covers job:resolve-issues/exact-head-ci
37
- // @level integration
38
- test('plans bind discovery, local reproduction, publish-only proof, exemptions, and skips to the frozen head', () => {
39
- const accepted = plan()
40
- assert.deepEqual(validateRequiredCheckPlan(accepted, { expectedHeadSha: head, requireEvidence: true }), { ok: true, errors: [] })
41
- assert.deepEqual(locallyReproducedJobs(accepted), ['skill-script-tests', 'skill-size'])
42
- assert.deepEqual(validateRequiredCheckExecution(accepted, [
43
- { name: 'skill-script-tests', conclusion: 'success' },
44
- { name: 'skill-size', conclusion: 'success' },
45
- { name: 'skill-script-tests-queue', conclusion: 'skipped' },
46
- ], { expectedHeadSha: head }), { ok: true, errors: [] })
47
- const exempted = plan()
48
- exempted.checks[2].admission = 'exempted'
49
- exempted.checks[2].changedPathExemption = { condition: '-files ~= ^skills/', applies: true }
50
- exempted.checks[2].evidence.result = 'skipped'
51
- assert.deepEqual(validateRequiredCheckPlan(exempted, { expectedHeadSha: head, requireEvidence: true }), { ok: true, errors: [] })
52
- const skipped = plan()
53
- skipped.checks[2].evidence.result = 'skipped'
54
- const rejected = validateRequiredCheckPlan(skipped, { expectedHeadSha: head, requireEvidence: true })
55
- assert.match(rejected.errors.join(' '), /skipped is never success/i)
56
- })
57
-
58
- // @covers job:resolve-issues/exact-head-ci
59
- // @level unit
60
- test('legacy required-job manifests receive migration guidance instead of a completeness claim', () => {
61
- const verdict = validateRequiredCheckPlan(undefined, { expectedHeadSha: head })
62
- assert.equal(verdict.ok, false)
63
- assert.match(verdict.errors[0], /legacy required-check manifest.*rediscover GitHub rulesets and Mergify conditions/i)
64
- })
65
-
66
- // @covers job:resolve-issues/exact-head-ci
67
- // @level unit
68
- // @fails-without-fix job:resolve-issues/exact-head-ci
69
- test('queue execution artifact top-level provider must bind to the planned queue evidence provider', () => {
70
- const queuePlan = {
71
- ...plan(),
72
- version: 2,
73
- checks: [{
74
- name: 'queue-only', admission: 'required', classification: 'publish-only', sources: ['mergify'], phases: ['queue-head'],
75
- evidence: { provider: 'github', reason: 'provider-created synthetic queue check' },
76
- queueEvidence: { provider: 'github', acquisition: 'github-check-runs' },
77
- }],
78
- }
79
- const artifact = {
80
- version: 1, kind: 'queue-execution', repository: '1aboveio/ai', pr: 42, prHeadSha: head,
81
- queuePr: 43, queueHeadSha: 'b'.repeat(40), provider: 'other-provider',
82
- checks: [{ name: 'queue-only', provider: 'github', conclusion: 'success', headSha: 'b'.repeat(40) }], status: 'pass',
83
- }
84
- const verdict = validateQueueExecutionArtifact(queuePlan, artifact, { expectedRepository: '1aboveio/ai', expectedPr: 42, expectedPrHeadSha: head })
85
- assert.equal(verdict.ok, false)
86
- assert.match(verdict.errors.join(' '), /queueExecutionArtifact\.provider.*expected github.*other-provider/i)
87
-
88
- const mixedProviderPlan = structuredClone(queuePlan)
89
- mixedProviderPlan.checks.push({
90
- ...mixedProviderPlan.checks[0], name: 'second-queue-only',
91
- queueEvidence: { provider: 'other-provider', acquisition: 'other-acquisition' },
92
- })
93
- const mixedVerdict = validateQueueExecutionArtifact(mixedProviderPlan, artifact, { expectedRepository: '1aboveio/ai', expectedPr: 42, expectedPrHeadSha: head })
94
- assert.equal(mixedVerdict.ok, false)
95
- assert.match(mixedVerdict.errors.join(' '), /exactly one non-empty provider across planned queue-head checks/i)
96
- })
@@ -1,96 +0,0 @@
1
- // Tests for the reference Next.js adapter's feature-derived signal detection
2
- // (issue #200). Runs the adapter as a subprocess against temp fixtures — proves
3
- // AC1 (emits signals) and AC5 (deterministic) without mocking. Run: `node --test`.
4
-
5
- import { test } from 'node:test'
6
- import assert from 'node:assert/strict'
7
- import fs from 'node:fs'
8
- import os from 'node:os'
9
- import path from 'node:path'
10
- import { fileURLToPath } from 'node:url'
11
- import { execFileSync, spawnSync } from 'node:child_process'
12
-
13
- const ADAPTER = fileURLToPath(new URL('./nextjs-inventory.mjs', import.meta.url))
14
-
15
- function runAdapter(appDir) {
16
- const out = path.join(appDir, '..', 'inv.json')
17
- execFileSync('node', [ADAPTER, appDir, '--out', out], { stdio: 'pipe' })
18
- return JSON.parse(fs.readFileSync(out, 'utf8'))
19
- }
20
-
21
- function fixture(pkg, files) {
22
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'nextadp-'))
23
- fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify(pkg))
24
- for (const [rel, content] of Object.entries(files)) {
25
- const p = path.join(dir, rel)
26
- fs.mkdirSync(path.dirname(p), { recursive: true })
27
- fs.writeFileSync(p, content)
28
- }
29
- return dir
30
- }
31
-
32
- test('detects themeToggle (next-themes dep) and i18n (next-intl dep) on every route', () => {
33
- const dir = fixture(
34
- { dependencies: { 'next-themes': '^0.2.0', 'next-intl': '^3.0.0' } },
35
- { 'app/page.tsx': 'export default function P(){return <div/>}',
36
- 'app/dashboard/page.tsx': 'export default function D(){return <div/>}' },
37
- )
38
- try {
39
- const inv = runAdapter(path.join(dir, 'app'))
40
- assert.equal(inv.surfaces.length, 2)
41
- for (const s of inv.surfaces) {
42
- assert.equal(s.signals?.themeToggle, true)
43
- assert.equal(s.signals?.i18n, true)
44
- }
45
- } finally { fs.rmSync(dir, { recursive: true, force: true }) }
46
- })
47
-
48
- test('detects themeToggle from source (useTheme) with no dep', () => {
49
- const dir = fixture(
50
- { dependencies: {} },
51
- { 'app/page.tsx': "import { useTheme } from 'somewhere'\nexport default function P(){ useTheme(); return <div/> }" },
52
- )
53
- try {
54
- const inv = runAdapter(path.join(dir, 'app'))
55
- assert.equal(inv.surfaces[0].signals?.themeToggle, true)
56
- assert.ok(!inv.surfaces[0].signals?.i18n)
57
- } finally { fs.rmSync(dir, { recursive: true, force: true }) }
58
- })
59
-
60
- test('no signals → no `signals` key (null, not an empty object)', () => {
61
- const dir = fixture(
62
- { dependencies: { react: '^19.0.0' } },
63
- { 'app/page.tsx': 'export default function P(){return <div/>}' },
64
- )
65
- try {
66
- const inv = runAdapter(path.join(dir, 'app'))
67
- assert.equal('signals' in inv.surfaces[0], false)
68
- } finally { fs.rmSync(dir, { recursive: true, force: true }) }
69
- })
70
-
71
- test('deterministic: two runs on the same code produce byte-identical output', () => {
72
- const dir = fixture(
73
- { dependencies: { 'next-themes': '^0.2.0' } },
74
- { 'app/page.tsx': 'export default function P(){return <div/>}',
75
- 'app/settings/page.tsx': "export default function S(){return <form onSubmit={()=>{}}/>}" },
76
- )
77
- try {
78
- const a = JSON.stringify(runAdapter(path.join(dir, 'app')))
79
- const b = JSON.stringify(runAdapter(path.join(dir, 'app')))
80
- assert.equal(a, b)
81
- } finally { fs.rmSync(dir, { recursive: true, force: true }) }
82
- })
83
-
84
- test('a flag or a missing directory in the appDir slot exits 2 and writes nothing', () => {
85
- const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'nextadp-cli-'))
86
- try {
87
- for (const args of [['--help'], ['--out', 'x.json'], [path.join(cwd, 'does-not-exist')]]) {
88
- const r = spawnSync('node', [ADAPTER, ...args], { cwd, encoding: 'utf8' })
89
- assert.equal(r.status, 2, `${args.join(' ')} → exit 2`)
90
- assert.match(r.stderr, /usage: nextjs-inventory\.mjs <appDir>/)
91
- }
92
- assert.deepEqual(fs.readdirSync(cwd), [], 'nothing was written into the cwd')
93
- } finally {
94
- fs.rmSync(cwd, { recursive: true, force: true })
95
- }
96
- })
@@ -1,47 +0,0 @@
1
- import { test } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import { scanText, isTestFile, runCli } from './check-quarantine-expiry.mjs'
4
-
5
- test('scanText flags an until= date in the past as expired, future as not', () => {
6
- const text = [
7
- '// @quarantine(until=2026-01-01, issue=#42) flaky payfields iframe',
8
- '// @quarantine(until=2027-12-31, issue=#99) external sandbox pending',
9
- ].join('\n')
10
- const recs = scanText('e2e/x.spec.ts', text, '2026-06-08')
11
- assert.equal(recs.length, 2)
12
- const past = recs.find((r) => r.until === '2026-01-01')
13
- assert.equal(past.expired, true)
14
- assert.equal(past.issue, '#42')
15
- assert.equal(past.line, 1)
16
- assert.equal(recs.find((r) => r.until === '2027-12-31').expired, false)
17
- })
18
-
19
- test('scanText matches yaml-style "until: DATE" too', () => {
20
- const recs = scanText('x.coverage.yml', 'suppress:\n - until: 2020-05-05', '2026-06-08')
21
- assert.equal(recs.length, 1)
22
- assert.equal(recs[0].expired, true)
23
- })
24
-
25
- test('boundary: until == now is NOT expired (expires strictly after the date)', () => {
26
- const recs = scanText('a.spec.ts', 'until=2026-06-08', '2026-06-08')
27
- assert.equal(recs[0].expired, false)
28
- })
29
-
30
- test('isTestFile recognizes spec/test/coverage files, not plain source', () => {
31
- assert.ok(isTestFile('e2e/login.spec.ts'))
32
- assert.ok(isTestFile('tests/foo.py'))
33
- assert.ok(isTestFile('req.coverage.yml'))
34
- assert.ok(!isTestFile('src/app.ts'))
35
- })
36
-
37
- test('runCli exits 1 on an expired exit, 0 when all future', () => {
38
- // Use this test file itself as a fixture-free input via a temp-ish path is overkill;
39
- // exercise the pure path through --files on real files in the repo dir instead.
40
- // Expired case: point at a string file we know — fall back to scanText-backed assertion.
41
- assert.equal(scanText('f', 'until=2000-01-01', '2026-06-08').filter((r) => r.expired).length, 1)
42
- assert.equal(scanText('f', 'until=2099-01-01', '2026-06-08').filter((r) => r.expired).length, 0)
43
- })
44
-
45
- test('runCli rejects a malformed --now', () => {
46
- assert.equal(runCli(['--now', 'yesterday', '--files', 'nope.ts']), 2)
47
- })