@1aboveio/skills 1.0.8 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +13 -0
- package/.codex-plugin/resources.json +1827 -0
- package/README.md +22 -0
- package/package.json +9 -2
- package/skills/app-dev/airflow/SKILL.md +1 -1
- package/skills/app-dev/airflow/release-check.mjs +3 -1
- package/skills/app-dev/android-foundation/SKILL.md +1 -1
- package/skills/app-dev/android-foundation/release-check.mjs +3 -1
- package/skills/app-dev/app-debug/SKILL.md +1 -1
- package/skills/app-dev/app-debug/release-check.mjs +3 -1
- package/skills/app-dev/better-auth/SKILL.md +1 -1
- package/skills/app-dev/better-auth/release-check.mjs +3 -1
- package/skills/app-dev/data-catalog/SKILL.md +1 -1
- package/skills/app-dev/data-catalog/release-check.mjs +3 -1
- package/skills/app-dev/data-quality/SKILL.md +1 -1
- package/skills/app-dev/data-quality/release-check.mjs +3 -1
- package/skills/app-dev/ios-foundation/SKILL.md +1 -1
- package/skills/app-dev/ios-foundation/release-check.mjs +3 -1
- package/skills/app-dev/monorepo/SKILL.md +1 -1
- package/skills/app-dev/monorepo/release-check.mjs +3 -1
- package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
- package/skills/app-dev/nextjs-fullstack/release-check.mjs +3 -1
- package/skills/app-dev/prisma-setup/SKILL.md +1 -1
- package/skills/app-dev/prisma-setup/release-check.mjs +3 -1
- package/skills/app-dev/pyspark/SKILL.md +1 -1
- package/skills/app-dev/pyspark/release-check.mjs +3 -1
- package/skills/app-dev/python-backend/SKILL.md +1 -1
- package/skills/app-dev/python-backend/release-check.mjs +3 -1
- package/skills/app-dev/shadcn/SKILL.md +1 -1
- package/skills/app-dev/shadcn/release-check.mjs +3 -1
- package/skills/app-dev/zod-v4/SKILL.md +1 -1
- package/skills/app-dev/zod-v4/release-check.mjs +3 -1
- package/skills/cicd/cloud-build/SKILL.md +1 -1
- package/skills/cicd/cloud-build/release-check.mjs +3 -1
- package/skills/cicd/cloud-debug/SKILL.md +1 -1
- package/skills/cicd/cloud-debug/release-check.mjs +3 -1
- package/skills/cicd/cloud-deploy/SKILL.md +1 -1
- package/skills/cicd/cloud-deploy/release-check.mjs +3 -1
- package/skills/cicd/docker/SKILL.md +1 -1
- package/skills/cicd/docker/release-check.mjs +3 -1
- package/skills/cicd/github-actions/SKILL.md +1 -1
- package/skills/cicd/github-actions/release-check.mjs +3 -1
- package/skills/cicd/github-flow/SKILL.md +1 -1
- package/skills/cicd/github-flow/release-check.mjs +3 -1
- package/skills/cicd/google-cloud/SKILL.md +1 -1
- package/skills/cicd/google-cloud/release-check.mjs +3 -1
- package/skills/cicd/mergify/SKILL.md +1 -1
- package/skills/cicd/mergify/release-check.mjs +3 -1
- package/skills/cicd/podman/SKILL.md +1 -1
- package/skills/cicd/podman/release-check.mjs +3 -1
- package/skills/content/article-review/SKILL.md +1 -1
- package/skills/content/article-review/release-check.mjs +3 -1
- package/skills/content/article-to-image/SKILL.md +1 -1
- package/skills/content/article-to-image/release-check.mjs +3 -1
- package/skills/content/flowchart-svg/SKILL.md +1 -1
- package/skills/content/flowchart-svg/release-check.mjs +3 -1
- package/skills/content/image-gen/SKILL.md +1 -1
- package/skills/content/image-gen/release-check.mjs +3 -1
- package/skills/content/mermaid/SKILL.md +1 -1
- package/skills/content/mermaid/release-check.mjs +3 -1
- package/skills/content/wechat-article-writer/SKILL.md +1 -1
- package/skills/content/wechat-article-writer/release-check.mjs +3 -1
- package/skills/content/wechat-publisher/SKILL.md +1 -1
- package/skills/content/wechat-publisher/release-check.mjs +3 -1
- package/skills/content/wechat-search/SKILL.md +1 -1
- package/skills/content/wechat-search/release-check.mjs +3 -1
- package/skills/content/xai-search/SKILL.md +88 -13
- package/skills/content/xai-search/release-check.mjs +3 -1
- package/skills/content/xai-search/scripts/xai_search.sh +125 -149
- package/skills/core/distill-writing/SKILL.md +1 -1
- package/skills/core/distill-writing/release-check.mjs +3 -1
- package/skills/core/doc-to-md/SKILL.md +1 -1
- package/skills/core/doc-to-md/release-check.mjs +3 -1
- package/skills/core/editorial-review/SKILL.md +1 -1
- package/skills/core/editorial-review/release-check.mjs +3 -1
- package/skills/core/house-style/SKILL.md +1 -1
- package/skills/core/house-style/release-check.mjs +3 -1
- package/skills/core/plain-language/SKILL.md +1 -1
- package/skills/core/plain-language/release-check.mjs +3 -1
- package/skills/core/polish-cn/SKILL.md +1 -1
- package/skills/core/polish-cn/release-check.mjs +3 -1
- package/skills/core/reg-parser/SKILL.md +1 -1
- package/skills/core/reg-parser/release-check.mjs +3 -1
- package/skills/core/terminology/SKILL.md +1 -1
- package/skills/core/terminology/release-check.mjs +3 -1
- package/skills/core/write-doc/SKILL.md +1 -1
- package/skills/core/write-doc/release-check.mjs +3 -1
- package/skills/engineering/better-review/SKILL.md +1 -1
- package/skills/engineering/better-review/release-check.mjs +3 -1
- package/skills/engineering/contributing/SKILL.md +1 -1
- package/skills/engineering/contributing/release-check.mjs +3 -1
- package/skills/engineering/e2e-test/SKILL.md +1 -1
- package/skills/engineering/e2e-test/release-check.mjs +3 -1
- package/skills/engineering/engineering-runtime/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/coherence/workflow.json +13 -13
- package/skills/engineering/engineering-runtime/release-check.mjs +3 -1
- package/skills/engineering/engineering-runtime/scripts/installation-resolver.mjs +191 -0
- package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +51 -10
- package/skills/engineering/ensure-coverage/SKILL.md +1 -1
- package/skills/engineering/ensure-coverage/release-check.mjs +3 -1
- package/skills/engineering/golden-path/SKILL.md +1 -1
- package/skills/engineering/golden-path/release-check.mjs +3 -1
- package/skills/engineering/harness-runtime/SKILL.md +1 -1
- package/skills/engineering/harness-runtime/release-check.mjs +3 -1
- package/skills/engineering/resolve-issues/SKILL.md +1 -1
- package/skills/engineering/resolve-issues/references/preflight.md +7 -0
- package/skills/engineering/resolve-issues/references/shared-modules.md +27 -0
- package/skills/engineering/resolve-issues/references/task-plan.md +5 -1
- package/skills/engineering/resolve-issues/release-check.mjs +3 -1
- package/skills/engineering/resolve-issues/scripts/active-installation.mjs +32 -0
- package/skills/engineering/resolve-issues/scripts/install-hooks.mjs +3 -0
- package/skills/engineering/resolve-issues/scripts/rehydrate.mjs +17 -1
- package/skills/engineering/resolve-release/SKILL.md +1 -1
- package/skills/engineering/resolve-release/release-check.mjs +3 -1
- package/skills/engineering/rush-issues/SKILL.md +1 -1
- package/skills/engineering/rush-issues/release-check.mjs +3 -1
- package/skills/engineering/rush-release/SKILL.md +1 -1
- package/skills/engineering/rush-release/release-check.mjs +3 -1
- package/skills/engineering/test-quality/SKILL.md +1 -1
- package/skills/engineering/test-quality/release-check.mjs +3 -1
- package/skills/risk-compliance/compliance-audit/SKILL.md +1 -1
- package/skills/risk-compliance/compliance-audit/release-check.mjs +3 -1
- package/skills/risk-compliance/contract-compare/SKILL.md +1 -1
- package/skills/risk-compliance/contract-compare/release-check.mjs +3 -1
- package/skills/risk-compliance/contract-review/SKILL.md +1 -1
- package/skills/risk-compliance/contract-review/release-check.mjs +3 -1
- package/skills/risk-compliance/doc-authenticity/SKILL.md +1 -1
- package/skills/risk-compliance/doc-authenticity/release-check.mjs +3 -1
- package/skills/risk-compliance/fraud-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/fraud-analysis/release-check.mjs +3 -1
- package/skills/risk-compliance/payment-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/payment-analysis/release-check.mjs +3 -1
- package/skills/risk-compliance/similar-domain/SKILL.md +1 -1
- package/skills/risk-compliance/similar-domain/release-check.mjs +3 -1
- package/skills/risk-compliance/tld-detection/SKILL.md +1 -1
- package/skills/risk-compliance/tld-detection/release-check.mjs +3 -1
- package/skills/app-dev/android-foundation/scripts/device-run.test.mjs +0 -436
- package/skills/app-dev/data-catalog/scripts/data-catalog.test.mjs +0 -36
- package/skills/app-dev/data-quality/scripts/data-quality.test.mjs +0 -12
- package/skills/cicd/github-actions/scripts/ci-run-audit.test.mjs +0 -637
- package/skills/cicd/github-flow/scripts/flow.test.mjs +0 -114
- package/skills/cicd/github-flow/scripts/persist-conventions.test.mjs +0 -108
- package/skills/cicd/mergify/scripts/audit.test.mjs +0 -201
- package/skills/cicd/mergify/scripts/entrypoints.test.mjs +0 -106
- package/skills/cicd/mergify/scripts/pr-620-mergify-comments.fixture.json +0 -30
- package/skills/cicd/mergify/scripts/stack-push.test.mjs +0 -233
- package/skills/cicd/mergify/scripts/watch-pr-delivery.test.mjs +0 -1826
- package/skills/content/image-gen/scripts/credentials.test.mjs +0 -212
- package/skills/content/image-gen/scripts/options.test.mjs +0 -245
- package/skills/content/xai-search/tests/smoke_test.sh +0 -36
- package/skills/core/polish-cn/scripts/polish-cn.test.mjs +0 -244
- package/skills/core/reg-parser/scripts/reg-parser.test.mjs +0 -165
- package/skills/engineering/better-review/scripts/pr-context.test.mjs +0 -168
- package/skills/engineering/better-review/scripts/rereview-scope.test.mjs +0 -587
- package/skills/engineering/better-review/scripts/scan-diff.test.mjs +0 -414
- package/skills/engineering/better-review/scripts/vendor-sync.test.mjs +0 -96
- package/skills/engineering/contributing/scripts/persist-conventions.test.mjs +0 -118
- package/skills/engineering/e2e-test/scripts/presentation-checks.test.mjs +0 -106
- package/skills/engineering/e2e-test/scripts/presentation-perceivability.test.mjs +0 -117
- package/skills/engineering/e2e-test/scripts/presentation-reachability.test.mjs +0 -69
- package/skills/engineering/e2e-test/scripts/presentation-render-health.test.mjs +0 -98
- package/skills/engineering/e2e-test/scripts/presentation-sweep.test.mjs +0 -71
- package/skills/engineering/e2e-test/scripts/presentation-temporal.test.mjs +0 -59
- package/skills/engineering/e2e-test/scripts/project-route-manifest.test.mjs +0 -76
- package/skills/engineering/engineering-runtime/engineering-runtime.test.mjs +0 -68
- package/skills/engineering/engineering-runtime/scripts/invocation-policy.test.mjs +0 -95
- package/skills/engineering/engineering-runtime/scripts/main-module.test.mjs +0 -288
- package/skills/engineering/engineering-runtime/scripts/required-check-plan.test.mjs +0 -96
- package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.test.mjs +0 -96
- package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.test.mjs +0 -47
- package/skills/engineering/ensure-coverage/scripts/ci-audit.test.mjs +0 -86
- package/skills/engineering/ensure-coverage/scripts/coverage-ledger.test.mjs +0 -447
- package/skills/engineering/ensure-coverage/scripts/design-parity.test.mjs +0 -552
- package/skills/engineering/ensure-coverage/scripts/lint-tests.test.mjs +0 -289
- package/skills/engineering/ensure-coverage/scripts/mock-policy-config.test.mjs +0 -119
- package/skills/engineering/golden-path/scripts/golden-path.test.mjs +0 -1457
- package/skills/engineering/harness-runtime/discover-models.test.mjs +0 -363
- package/skills/engineering/harness-runtime/harness-runtime.test.mjs +0 -967
- package/skills/engineering/harness-runtime/model-catalog-seed.test.mjs +0 -829
- package/skills/engineering/harness-runtime/model-catalog.test.mjs +0 -366
- package/skills/engineering/resolve-issues/scripts/cleanup-runs.test.mjs +0 -280
- package/skills/engineering/resolve-issues/scripts/dropbox.test.mjs +0 -346
- package/skills/engineering/resolve-issues/scripts/findings-reuse.test.mjs +0 -88
- package/skills/engineering/resolve-issues/scripts/github-reviews.test.mjs +0 -264
- package/skills/engineering/resolve-issues/scripts/install-hooks.test.mjs +0 -79
- package/skills/engineering/resolve-issues/scripts/issue-tree.test.mjs +0 -107
- package/skills/engineering/resolve-issues/scripts/manual-invocation.test.mjs +0 -14
- package/skills/engineering/resolve-issues/scripts/next-actions-capacity.test.mjs +0 -149
- package/skills/engineering/resolve-issues/scripts/next-actions.test.mjs +0 -915
- package/skills/engineering/resolve-issues/scripts/plan-sync.test.mjs +0 -156
- package/skills/engineering/resolve-issues/scripts/preflight-models.test.mjs +0 -448
- package/skills/engineering/resolve-issues/scripts/ready-check.test.mjs +0 -217
- package/skills/engineering/resolve-issues/scripts/rehydrate.test.mjs +0 -91
- package/skills/engineering/resolve-issues/scripts/review-verdict.test.mjs +0 -1060
- package/skills/engineering/resolve-issues/scripts/size-check.test.mjs +0 -458
- package/skills/engineering/resolve-release/scripts/apply.test.mjs +0 -99
- package/skills/engineering/resolve-release/scripts/dark-flags.test.mjs +0 -100
- package/skills/engineering/resolve-release/scripts/flow.test.mjs +0 -340
- package/skills/engineering/resolve-release/scripts/green-head.test.mjs +0 -101
- package/skills/engineering/resolve-release/scripts/npm-version-absence.test.mjs +0 -426
- package/skills/engineering/resolve-release/scripts/plan-npm.test.mjs +0 -659
- package/skills/engineering/resolve-release/scripts/plan-toml-boundary.test.mjs +0 -317
- package/skills/engineering/resolve-release/scripts/plan-uv.test.mjs +0 -291
- package/skills/engineering/resolve-release/scripts/plan.test.mjs +0 -428
- package/skills/engineering/test-quality/scripts/lint-tests.test.mjs +0 -591
- package/skills/engineering/test-quality/scripts/mock-policy-config.test.mjs +0 -119
- package/skills/engineering/test-quality/scripts/persist-conventions.test.mjs +0 -104
- package/skills/risk-compliance/compliance-audit/scripts/compliance-audit.test.mjs +0 -14
- package/skills/risk-compliance/doc-authenticity/manual-invocation.test.mjs +0 -12
- package/skills/risk-compliance/doc-authenticity/scripts/validate_verdict.test.mjs +0 -212
- package/skills/risk-compliance/tld-detection/scripts/detect_ladders.test.mjs +0 -14
- package/skills/risk-compliance/tld-detection/scripts/detect_streams.test.mjs +0 -14
- package/skills/risk-compliance/tld-detection/tests/test_detect_ladders.py +0 -134
- package/skills/risk-compliance/tld-detection/tests/test_detect_streams.py +0 -520
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
// Tests for model-catalog — pure core + CLI round-trip (exit codes), no network.
|
|
2
|
-
import { test } from 'node:test'
|
|
3
|
-
import assert from 'node:assert/strict'
|
|
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
|
-
import { spawnSync } from 'node:child_process'
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
PRUNE_AFTER_DAYS, syncCatalog, bankEntry, readCatalog, writeCatalog, pathForHarness,
|
|
12
|
-
EFFORTS, DEFAULT_EFFORT, TIERS, SLOT_ELIGIBLE_TIERS, normalizeTier, operatingPoints, tierAt, toTiers,
|
|
13
|
-
} from './model-catalog.mjs'
|
|
14
|
-
|
|
15
|
-
const SCRIPT = fileURLToPath(new URL('./bin/model-catalog.mjs', import.meta.url))
|
|
16
|
-
const tmpFile = () => path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'model-catalog-test-')), 'catalog.json')
|
|
17
|
-
|
|
18
|
-
const entry = (id, lastSeenAt, confirmedAt = '2026-01-01') => ({ id, family: 'x', tier: 'frontier', confirmedAt, ...(lastSeenAt ? { lastSeenAt } : {}) })
|
|
19
|
-
|
|
20
|
-
// ---- syncCatalog: the rule-4 predicate in one place ----
|
|
21
|
-
|
|
22
|
-
// @covers package:harness-runtime
|
|
23
|
-
// @level unit
|
|
24
|
-
test('sync bumps lastSeenAt for seen ids and keeps fresh unseen entries', () => {
|
|
25
|
-
const { catalog, bumped, pruned } = syncCatalog(
|
|
26
|
-
{ models: [entry('a', '2026-05-01'), entry('b', '2026-06-01')] },
|
|
27
|
-
['a'],
|
|
28
|
-
'2026-07-13',
|
|
29
|
-
)
|
|
30
|
-
assert.deepEqual(bumped, ['a'])
|
|
31
|
-
assert.deepEqual(pruned, [])
|
|
32
|
-
assert.equal(catalog.models.find((m) => m.id === 'a').lastSeenAt, '2026-07-13')
|
|
33
|
-
assert.equal(catalog.models.find((m) => m.id === 'b').lastSeenAt, '2026-06-01') // unseen: untouched
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
test('sync prunes strictly after PRUNE_AFTER_DAYS — day 60 kept, day 61 pruned', () => {
|
|
37
|
-
// 2026-05-14 is exactly 60 days before 2026-07-13; 2026-05-13 is 61.
|
|
38
|
-
const { catalog, pruned } = syncCatalog(
|
|
39
|
-
{ models: [entry('kept', '2026-05-14'), entry('gone', '2026-05-13')] },
|
|
40
|
-
[],
|
|
41
|
-
'2026-07-13',
|
|
42
|
-
)
|
|
43
|
-
assert.equal(PRUNE_AFTER_DAYS, 60)
|
|
44
|
-
assert.deepEqual(pruned, ['gone'])
|
|
45
|
-
assert.deepEqual(catalog.models.map((m) => m.id), ['kept'])
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
test('sync falls back to confirmedAt when lastSeenAt is absent (pre-field entries)', () => {
|
|
49
|
-
const { pruned } = syncCatalog(
|
|
50
|
-
{ models: [entry('old', null, '2026-01-01'), entry('new', null, '2026-07-01')] },
|
|
51
|
-
[],
|
|
52
|
-
'2026-07-13',
|
|
53
|
-
)
|
|
54
|
-
assert.deepEqual(pruned, ['old'])
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
test('sync bumps before pruning — a stale entry rediscovered this run survives', () => {
|
|
58
|
-
const { catalog, pruned } = syncCatalog(
|
|
59
|
-
{ models: [entry('back', '2026-01-01')] },
|
|
60
|
-
['back'],
|
|
61
|
-
'2026-07-13',
|
|
62
|
-
)
|
|
63
|
-
assert.deepEqual(pruned, [])
|
|
64
|
-
assert.equal(catalog.models[0].lastSeenAt, '2026-07-13')
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
// ---- bankEntry: rule-2 classification writes ----
|
|
68
|
-
|
|
69
|
-
test('bank appends a new entry stamped with confirmedAt + lastSeenAt', () => {
|
|
70
|
-
const c = bankEntry({ models: [] }, { id: 'm', family: 'openai', tier: 'balanced-coder' }, '2026-07-13')
|
|
71
|
-
assert.deepEqual(c.models, [{ id: 'm', family: 'openai', tiers: { high: 'balanced-coder' }, confirmedAt: '2026-07-13', lastSeenAt: '2026-07-13' }])
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
test('bank overwrites an existing id (human correction) and refreshes confirmedAt', () => {
|
|
75
|
-
const c = bankEntry(
|
|
76
|
-
{ models: [entry('m', '2026-06-01')] },
|
|
77
|
-
{ id: 'm', family: 'openai', tier: 'balanced-coder' },
|
|
78
|
-
'2026-07-13',
|
|
79
|
-
)
|
|
80
|
-
assert.equal(c.models.length, 1)
|
|
81
|
-
assert.deepEqual(c.models[0].tiers, { high: 'balanced-coder' })
|
|
82
|
-
assert.equal(c.models[0].confirmedAt, '2026-07-13')
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
test('bank rejects entries missing id/family/tier or with an unknown tier', () => {
|
|
86
|
-
assert.throws(() => bankEntry({ models: [] }, { family: 'x', tier: 'frontier' }, '2026-07-13'))
|
|
87
|
-
assert.throws(() => bankEntry({ models: [] }, { id: 'm', tier: 'frontier' }, '2026-07-13'))
|
|
88
|
-
assert.throws(() => bankEntry({ models: [] }, { id: 'm', family: 'x' }, '2026-07-13')) // neither tier nor tiers
|
|
89
|
-
assert.throws(() => bankEntry({ models: [] }, { id: 'm', family: 'x', tier: 'huge' }, '2026-07-13'))
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
// ---- operating points: tier = f(model, effort), banked once (2026-07-19) ----
|
|
93
|
-
|
|
94
|
-
test('bank accepts a tiers map — one model, two operating points', () => {
|
|
95
|
-
const c = bankEntry(
|
|
96
|
-
{ models: [] },
|
|
97
|
-
{ id: 'gpt-5.6-terra', family: 'openai', tiers: { high: 'balanced-coder', max: 'deep-reasoner' } },
|
|
98
|
-
'2026-07-19',
|
|
99
|
-
)
|
|
100
|
-
assert.deepEqual(c.models[0].tiers, { high: 'balanced-coder', max: 'deep-reasoner' })
|
|
101
|
-
assert.equal(c.models[0].tier, undefined) // the legacy single-point field never persists
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
test('a single tier UPSERTS one point; a full tiers map REPLACES all of them', () => {
|
|
105
|
-
let c = bankEntry({ models: [] }, { id: 'terra', family: 'openai', tier: 'balanced-coder' }, '2026-07-19')
|
|
106
|
-
assert.deepEqual(c.models[0].tiers, { high: 'balanced-coder' }) // no effort -> the high point
|
|
107
|
-
|
|
108
|
-
// measured later, confirmed on its own: the existing point survives
|
|
109
|
-
c = bankEntry(c, { id: 'terra', family: 'openai', tier: 'deep-reasoner', effort: 'max' }, '2026-07-19')
|
|
110
|
-
assert.deepEqual(c.models[0].tiers, { high: 'balanced-coder', max: 'deep-reasoner' })
|
|
111
|
-
|
|
112
|
-
// a full map is the human restating the whole picture — it wins outright
|
|
113
|
-
c = bankEntry(c, { id: 'terra', family: 'openai', tiers: { max: 'deep-reasoner' } }, '2026-07-19')
|
|
114
|
-
assert.deepEqual(c.models[0].tiers, { max: 'deep-reasoner' })
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
test('an upsert onto a LEGACY-shape entry leaves no stale tier/effort behind', () => {
|
|
118
|
-
// Regression: the strip ran on the incoming entry only, so `existing.tier` was
|
|
119
|
-
// spread back in with nothing to shadow it — the stored entry then claimed a tier
|
|
120
|
-
// that could flatly contradict its own map. Reads went through toTiers and looked
|
|
121
|
-
// fine; the FILE was wrong, and the file is what a human and a cross-harness
|
|
122
|
-
// lookup (catalog rule 5) actually read.
|
|
123
|
-
const legacy = { models: [{ id: 'm', family: 'f', tier: 'mid-tier', effort: 'high', confirmedAt: '2026-06-01' }] }
|
|
124
|
-
|
|
125
|
-
const upserted = bankEntry(legacy, { id: 'm', family: 'f', tier: 'deep-reasoner', effort: 'max' }, '2026-07-19')
|
|
126
|
-
assert.equal(upserted.models[0].tier, undefined)
|
|
127
|
-
assert.equal(upserted.models[0].effort, undefined)
|
|
128
|
-
assert.deepEqual(upserted.models[0].tiers, { high: 'balanced-coder', max: 'deep-reasoner' }) // legacy point preserved
|
|
129
|
-
|
|
130
|
-
const replaced = bankEntry({ models: [{ id: 'm', family: 'f', tier: 'frontier' }] }, { id: 'm', family: 'f', tiers: { high: 'balanced-coder' } }, '2026-07-19')
|
|
131
|
-
assert.equal(replaced.models[0].tier, undefined) // never "frontier" beside a balanced-coder map
|
|
132
|
-
assert.deepEqual(replaced.models[0].tiers, { high: 'balanced-coder' })
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
test('bank refuses an entry carrying no operating points', () => {
|
|
136
|
-
// `tiers: {}` passes a truthiness guard but classifies nothing: tierAt returns
|
|
137
|
-
// null at every effort and no later sync ever flags it. Check the POINTS.
|
|
138
|
-
for (const bad of [{}, [], null]) {
|
|
139
|
-
assert.throws(
|
|
140
|
-
() => bankEntry({ models: [] }, { id: 'a', family: 'f', tiers: bad }, '2026-07-19'),
|
|
141
|
-
/tiers|operating points/,
|
|
142
|
-
`tiers: ${JSON.stringify(bad)} must be rejected`,
|
|
143
|
-
)
|
|
144
|
-
}
|
|
145
|
-
assert.throws(() => bankEntry({ models: [] }, { id: 'a', family: 'f', tiers: 'frontier' }, '2026-07-19'), /tiers/)
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
test('sync does not emit an undefined tier for an entry with no classification at all', () => {
|
|
149
|
-
const { migrated } = syncCatalog({ models: [{ id: 'nocap', family: 'f', lastSeenAt: '2026-07-18' }] }, [], '2026-07-19')
|
|
150
|
-
assert.ok(!migrated.some((m) => m.includes('undefined')), `migration line leaked undefined: ${migrated}`)
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
test('bank rejects a point below high — cheaper means a cheaper model, not a throttled one', () => {
|
|
154
|
-
for (const bad of ['low', 'medium', 'none']) {
|
|
155
|
-
assert.throws(
|
|
156
|
-
() => bankEntry({ models: [] }, { id: 'm', family: 'x', tiers: { [bad]: 'balanced-coder' } }, '2026-07-19'),
|
|
157
|
-
/effort/,
|
|
158
|
-
`effort "${bad}" must be rejected`,
|
|
159
|
-
)
|
|
160
|
-
}
|
|
161
|
-
assert.throws(() => bankEntry({ models: [] }, { id: 'm', family: 'x', tiers: { high: 'huge' } }, '2026-07-19'), /tier/)
|
|
162
|
-
assert.deepEqual(EFFORTS, ['high', 'xhigh', 'max'])
|
|
163
|
-
assert.equal(DEFAULT_EFFORT, 'high')
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
test('operatingPoints lists every banked point, strongest tier first', () => {
|
|
167
|
-
const catalog = {
|
|
168
|
-
models: [
|
|
169
|
-
{ id: 'terra', family: 'openai', tiers: { high: 'balanced-coder', max: 'deep-reasoner' } },
|
|
170
|
-
{ id: 'sonnet', family: 'anthropic', tiers: { high: 'balanced-coder' } },
|
|
171
|
-
],
|
|
172
|
-
}
|
|
173
|
-
// slotEligible rides along on every point (wired to SLOT_ELIGIBLE_TIERS in 2026-07-20's
|
|
174
|
-
// audit fix); both bands here are eligible, the scoped-coder case lives in the seed tests.
|
|
175
|
-
assert.deepEqual(operatingPoints(catalog, 'terra'), [
|
|
176
|
-
{ effort: 'max', tier: 'deep-reasoner', slotEligible: true },
|
|
177
|
-
{ effort: 'high', tier: 'balanced-coder', slotEligible: true },
|
|
178
|
-
])
|
|
179
|
-
assert.deepEqual(operatingPoints(catalog, 'sonnet'), [{ effort: 'high', tier: 'balanced-coder', slotEligible: true }])
|
|
180
|
-
assert.deepEqual(operatingPoints(catalog, 'unknown'), [])
|
|
181
|
-
assert.deepEqual(operatingPoints(null, 'terra'), []) // absent catalog is normal
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
test('tierAt answers what a model reaches at a given effort, null where nothing is banked', () => {
|
|
185
|
-
const catalog = { models: [{ id: 'terra', family: 'openai', tiers: { high: 'balanced-coder', max: 'deep-reasoner' } }] }
|
|
186
|
-
assert.equal(tierAt(catalog, 'terra', 'max'), 'deep-reasoner')
|
|
187
|
-
assert.equal(tierAt(catalog, 'terra', 'high'), 'balanced-coder')
|
|
188
|
-
assert.equal(tierAt(catalog, 'terra', 'xhigh'), null) // not measured -> not claimed
|
|
189
|
-
assert.equal(tierAt(catalog, 'nope', 'high'), null)
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
test('bank accepts deep-reasoner (the band below the current top end)', () => {
|
|
193
|
-
const c = bankEntry({ models: [] }, { id: 'gpt-5.5', family: 'openai', tier: 'deep-reasoner' }, '2026-07-13')
|
|
194
|
-
assert.deepEqual(c.models[0].tiers, { high: 'deep-reasoner' })
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
// ---- tier renames (2026-07-19): prior-frontier -> deep-reasoner, mid-tier -> balanced-coder ----
|
|
198
|
-
|
|
199
|
-
test('bank accepts either legacy tier name and stores the current one', () => {
|
|
200
|
-
const a = bankEntry({ models: [] }, { id: 'opus', family: 'anthropic', tier: 'prior-frontier' }, '2026-07-19')
|
|
201
|
-
assert.deepEqual(a.models[0].tiers, { high: 'deep-reasoner' })
|
|
202
|
-
const b = bankEntry({ models: [] }, { id: 'sonnet', family: 'anthropic', tier: 'mid-tier', effort: 'max' }, '2026-07-19')
|
|
203
|
-
assert.deepEqual(b.models[0].tiers, { max: 'balanced-coder' })
|
|
204
|
-
assert.deepEqual(toTiers({ tiers: { high: 'prior-frontier' } }), { high: 'deep-reasoner' }) // legacy names inside a map too
|
|
205
|
-
assert.deepEqual(TIERS, ['frontier', 'deep-reasoner', 'balanced-coder', 'scoped-coder'])
|
|
206
|
-
for (const t of TIERS) assert.equal(normalizeTier(t), t) // current names pass through
|
|
207
|
-
})
|
|
208
|
-
|
|
209
|
-
test('sync migrates legacy tier names AND the single-point shape, reporting both', () => {
|
|
210
|
-
const { catalog, migrated } = syncCatalog(
|
|
211
|
-
{
|
|
212
|
-
models: [
|
|
213
|
-
{ id: 'opus', family: 'anthropic', tier: 'prior-frontier', lastSeenAt: '2026-07-18' },
|
|
214
|
-
{ id: 'terra', family: 'openai', tier: 'mid-tier', effort: 'max', lastSeenAt: '2026-07-18' },
|
|
215
|
-
{ id: 'fable', family: 'anthropic', tiers: { high: 'frontier' }, lastSeenAt: '2026-07-18' },
|
|
216
|
-
],
|
|
217
|
-
},
|
|
218
|
-
['opus'],
|
|
219
|
-
'2026-07-19',
|
|
220
|
-
)
|
|
221
|
-
// opus: renamed value + reshaped; terra: renamed value at its banked effort;
|
|
222
|
-
// fable: already current -> absent from the report entirely.
|
|
223
|
-
assert.deepEqual(migrated, [
|
|
224
|
-
'opus: {high:prior-frontier} -> {high:deep-reasoner}',
|
|
225
|
-
'terra: {max:mid-tier} -> {max:balanced-coder}',
|
|
226
|
-
])
|
|
227
|
-
assert.deepEqual(catalog.models.find((m) => m.id === 'opus').tiers, { high: 'deep-reasoner' })
|
|
228
|
-
assert.deepEqual(catalog.models.find((m) => m.id === 'terra').tiers, { max: 'balanced-coder' })
|
|
229
|
-
assert.deepEqual(catalog.models.find((m) => m.id === 'fable').tiers, { high: 'frontier' })
|
|
230
|
-
for (const m of catalog.models) {
|
|
231
|
-
assert.equal(m.tier, undefined) // the legacy fields are gone, not merely shadowed
|
|
232
|
-
assert.equal(m.effort, undefined)
|
|
233
|
-
}
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
test('sync reshapes a current-name single-point entry without claiming a rename', () => {
|
|
237
|
-
const { migrated } = syncCatalog(
|
|
238
|
-
{ models: [{ id: 'sonnet', family: 'anthropic', tier: 'balanced-coder', lastSeenAt: '2026-07-18' }] },
|
|
239
|
-
[],
|
|
240
|
-
'2026-07-19',
|
|
241
|
-
)
|
|
242
|
-
assert.deepEqual(migrated, ['sonnet: tier/effort -> tiers map {high:balanced-coder}'])
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
test('sync migrates a legacy entry even when it never saw it', () => {
|
|
246
|
-
const { migrated } = syncCatalog(
|
|
247
|
-
{ models: [{ id: 'unseen', family: 'x', tier: 'prior-frontier', lastSeenAt: '2026-07-01' }] },
|
|
248
|
-
[], // migration is not gated on discovery — it is a rename, not an observation
|
|
249
|
-
'2026-07-19',
|
|
250
|
-
)
|
|
251
|
-
assert.deepEqual(migrated, ['unseen: {high:prior-frontier} -> {high:deep-reasoner}'])
|
|
252
|
-
})
|
|
253
|
-
|
|
254
|
-
// ---- CLI round-trip ----
|
|
255
|
-
|
|
256
|
-
function run(args, input) {
|
|
257
|
-
return spawnSync('node', [SCRIPT, ...args], { encoding: 'utf8', input })
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
test('CLI: read/sync exit 3 on an absent catalog (absent is normal)', () => {
|
|
261
|
-
const file = tmpFile()
|
|
262
|
-
assert.equal(run(['read', '--file', file]).status, 3)
|
|
263
|
-
assert.equal(run(['sync', '--file', file, '--seen', 'a']).status, 3)
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
// @covers package:harness-runtime
|
|
267
|
-
// @level integration
|
|
268
|
-
test('CLI: bank creates the file; sync bumps + prunes and reports both', () => {
|
|
269
|
-
const file = tmpFile()
|
|
270
|
-
assert.equal(run(['bank', JSON.stringify({ id: 'a', family: 'x', tier: 'frontier' }), '--file', file, '--today', '2026-01-01']).status, 0)
|
|
271
|
-
assert.equal(run(['bank', JSON.stringify({ id: 'b', family: 'y', tier: 'balanced-coder' }), '--file', file, '--today', '2026-07-01']).status, 0)
|
|
272
|
-
|
|
273
|
-
const sync = run(['sync', '--file', file, '--seen', 'b', '--today', '2026-07-13'])
|
|
274
|
-
assert.equal(sync.status, 0)
|
|
275
|
-
assert.deepEqual(JSON.parse(sync.stdout), { bumped: ['b'], pruned: ['a'], migrated: [] }) // a: unseen since Jan — pruned
|
|
276
|
-
|
|
277
|
-
const kept = readCatalog(file)
|
|
278
|
-
assert.deepEqual(kept.models.map((m) => m.id), ['b'])
|
|
279
|
-
assert.equal(kept.models[0].lastSeenAt, '2026-07-13')
|
|
280
|
-
})
|
|
281
|
-
|
|
282
|
-
test('CLI: list aliases read, help exits 0', () => {
|
|
283
|
-
const file = tmpFile()
|
|
284
|
-
assert.equal(run(['bank', JSON.stringify({ id: 'a', family: 'x', tier: 'frontier' }), '--file', file, '--today', '2026-07-13']).status, 0)
|
|
285
|
-
const list = run(['list', '--file', file])
|
|
286
|
-
assert.equal(list.status, 0)
|
|
287
|
-
assert.equal(JSON.parse(list.stdout).models[0].id, 'a')
|
|
288
|
-
assert.equal(run(['help']).status, 0)
|
|
289
|
-
assert.equal(run(['bogus']).status, 2)
|
|
290
|
-
})
|
|
291
|
-
|
|
292
|
-
test('CLI: bank via --stdin round-trips through read', () => {
|
|
293
|
-
const file = tmpFile()
|
|
294
|
-
assert.equal(run(['bank', '--stdin', '--file', file, '--today', '2026-07-13'], JSON.stringify({ id: 's', family: 'x', tier: 'frontier' })).status, 0)
|
|
295
|
-
const read = run(['read', '--file', file])
|
|
296
|
-
assert.equal(read.status, 0)
|
|
297
|
-
assert.equal(JSON.parse(read.stdout).models[0].id, 's')
|
|
298
|
-
})
|
|
299
|
-
|
|
300
|
-
// ---- per-harness catalogs (one file per harness, never shared) ----
|
|
301
|
-
|
|
302
|
-
// @covers package:harness-runtime
|
|
303
|
-
// @level unit
|
|
304
|
-
test('pathForHarness maps the known harnesses and returns null for unknown ones', () => {
|
|
305
|
-
assert.equal(pathForHarness('claude-code', { HOME: '/h' }), '/h/.claude/model-catalog.json')
|
|
306
|
-
assert.equal(pathForHarness('codex', { HOME: '/h' }), '/h/.codex/model-catalog.json')
|
|
307
|
-
assert.equal(pathForHarness('pi', { HOME: '/h' }), '/h/.pi/agent/model-catalog.json')
|
|
308
|
-
assert.equal(pathForHarness('bogus', { HOME: '/h' }), null)
|
|
309
|
-
})
|
|
310
|
-
|
|
311
|
-
test('CLI: bank --harness stamps the file; a write from another harness is refused', () => {
|
|
312
|
-
const file = tmpFile()
|
|
313
|
-
assert.equal(run(['bank', JSON.stringify({ id: 'a', family: 'x', tier: 'frontier' }), '--file', file, '--harness', 'codex', '--today', '2026-07-13']).status, 0)
|
|
314
|
-
assert.equal(readCatalog(file).harness, 'codex')
|
|
315
|
-
|
|
316
|
-
// same file, different harness → refused (exit 2), catalog untouched
|
|
317
|
-
assert.equal(run(['sync', '--file', file, '--harness', 'pi', '--seen', 'a', '--today', '2026-07-13']).status, 2)
|
|
318
|
-
assert.equal(run(['bank', JSON.stringify({ id: 'b', family: 'y', tier: 'balanced-coder' }), '--file', file, '--harness', 'pi', '--today', '2026-07-13']).status, 2)
|
|
319
|
-
assert.deepEqual(readCatalog(file).models.map((m) => m.id), ['a'])
|
|
320
|
-
|
|
321
|
-
// matching harness → fine
|
|
322
|
-
assert.equal(run(['sync', '--file', file, '--harness', 'codex', '--seen', 'a', '--today', '2026-07-13']).status, 0)
|
|
323
|
-
})
|
|
324
|
-
|
|
325
|
-
test('CLI: bank --harness stamps a legacy (pre-field) file at its next confirmed write', () => {
|
|
326
|
-
const file = tmpFile()
|
|
327
|
-
writeCatalog(file, { models: [entry('a', '2026-07-01')] }) // no harness stamp
|
|
328
|
-
assert.equal(run(['bank', JSON.stringify({ id: 'b', family: 'y', tier: 'balanced-coder' }), '--file', file, '--harness', 'pi', '--today', '2026-07-13']).status, 0)
|
|
329
|
-
assert.equal(readCatalog(file).harness, 'pi')
|
|
330
|
-
})
|
|
331
|
-
|
|
332
|
-
test('CLI: --harness with no location mapping errors, path --harness resolves', () => {
|
|
333
|
-
assert.equal(run(['read', '--harness', 'bogus']).status, 2)
|
|
334
|
-
const p = run(['path', '--harness', 'pi'])
|
|
335
|
-
assert.equal(p.status, 0)
|
|
336
|
-
assert.ok(p.stdout.trim().endsWith(path.join('.pi', 'agent', 'model-catalog.json')))
|
|
337
|
-
})
|
|
338
|
-
|
|
339
|
-
test('writeCatalog is atomic-ish: file parses after write, tmp file removed', () => {
|
|
340
|
-
const file = tmpFile()
|
|
341
|
-
writeCatalog(file, { models: [] })
|
|
342
|
-
assert.deepEqual(JSON.parse(fs.readFileSync(file, 'utf8')), { models: [] })
|
|
343
|
-
assert.ok(!fs.existsSync(`${file}.tmp`))
|
|
344
|
-
})
|
|
345
|
-
|
|
346
|
-
test('scoped-coder is a bankable classification but never a slot candidate', () => {
|
|
347
|
-
// The band exists so a model that keeps turning up in discovery, but sits below the
|
|
348
|
-
// implementer floor, can be answered ONCE instead of being tagged
|
|
349
|
-
// "unconfirmed — please classify" every run. It must bank like any other tier...
|
|
350
|
-
const c = bankEntry({ models: [] }, { id: 'gemini-3.5-flash', family: 'google', tier: 'scoped-coder' }, '2026-07-20')
|
|
351
|
-
assert.deepEqual(c.models[0].tiers, { high: 'scoped-coder' })
|
|
352
|
-
|
|
353
|
-
// ...and must never reach a slot.
|
|
354
|
-
assert.ok(TIERS.includes('scoped-coder'), 'bankable')
|
|
355
|
-
assert.ok(!SLOT_ELIGIBLE_TIERS.includes('scoped-coder'), 'not proposable for implementer/reviewer')
|
|
356
|
-
assert.deepEqual(SLOT_ELIGIBLE_TIERS, ['frontier', 'deep-reasoner', 'balanced-coder'])
|
|
357
|
-
})
|
|
358
|
-
|
|
359
|
-
test('scoped-coder sorts below every slot-eligible band', () => {
|
|
360
|
-
// operatingPoints sorts by TIERS.indexOf, so a mis-ordered enum would float a
|
|
361
|
-
// scoped-coder above a balanced-coder in the proposal block and invite a bad pick.
|
|
362
|
-
assert.equal(TIERS.indexOf('scoped-coder'), TIERS.length - 1)
|
|
363
|
-
for (const t of SLOT_ELIGIBLE_TIERS) {
|
|
364
|
-
assert.ok(TIERS.indexOf(t) < TIERS.indexOf('scoped-coder'), `${t} must outrank scoped-coder`)
|
|
365
|
-
}
|
|
366
|
-
})
|
|
@@ -1,280 +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 { chmodSync, existsSync, mkdirSync, mkdtempSync, readdirSync, utimesSync, 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 { EVIDENCE_MAX_BYTES, planSweep } from './cleanup-runs.mjs'
|
|
10
|
-
|
|
11
|
-
const script = join(dirname(fileURLToPath(import.meta.url)), 'cleanup-runs.mjs')
|
|
12
|
-
const DAY_MS = 24 * 60 * 60 * 1000
|
|
13
|
-
|
|
14
|
-
const git = (dir, ...args) => execFileSync('git', ['-C', dir, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] })
|
|
15
|
-
|
|
16
|
-
function write(path, content = 'x') {
|
|
17
|
-
mkdirSync(dirname(path), { recursive: true })
|
|
18
|
-
writeFileSync(path, content)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// A clone of the remote at `dir`, with ignored installs, caches, and a test log.
|
|
22
|
-
function clone(remote, dir) {
|
|
23
|
-
git(dirname(dir), 'clone', '--quiet', remote, dir)
|
|
24
|
-
git(dir, 'config', 'user.email', 't@example.com')
|
|
25
|
-
git(dir, 'config', 'user.name', 'T')
|
|
26
|
-
write(join(dir, 'node_modules', 'pkg', 'package.json'), '{}')
|
|
27
|
-
write(join(dir, '.cache', 'sdk', 'system.img'), 'big')
|
|
28
|
-
write(join(dir, 'test-results', 'journey.log'), 'emulator boot failed')
|
|
29
|
-
write(join(dir, 'test-results', 'avd', 'Charge.avd', 'userdata.img'), 'big')
|
|
30
|
-
return dir
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function fixture() {
|
|
34
|
-
const base = mkdtempSync(join(tmpdir(), 'cleanup-runs-'))
|
|
35
|
-
const remote = join(base, 'remote.git')
|
|
36
|
-
const seed = join(base, 'seed')
|
|
37
|
-
execFileSync('git', ['init', '--quiet', '--bare', remote])
|
|
38
|
-
execFileSync('git', ['init', '--quiet', seed])
|
|
39
|
-
git(seed, 'config', 'user.email', 't@example.com')
|
|
40
|
-
git(seed, 'config', 'user.name', 'T')
|
|
41
|
-
write(join(seed, '.gitignore'), 'node_modules/\n.cache/\ntest-results/\n')
|
|
42
|
-
write(join(seed, 'app.js'), 'v1')
|
|
43
|
-
write(join(seed, 'docs', 'README.md'), '# tracked doc')
|
|
44
|
-
git(seed, 'add', '.')
|
|
45
|
-
git(seed, 'commit', '--quiet', '-m', 'seed')
|
|
46
|
-
git(seed, 'push', '--quiet', remote, 'HEAD:main')
|
|
47
|
-
git(remote, 'symbolic-ref', 'HEAD', 'refs/heads/main')
|
|
48
|
-
const repo = join(base, 'repo')
|
|
49
|
-
mkdirSync(repo)
|
|
50
|
-
return { base, remote, repo, rush: join(repo, '.resolve-issues', 'rush') }
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function makeRun(rush, name, idleDays) {
|
|
54
|
-
const dir = join(rush, name)
|
|
55
|
-
write(join(dir, 'task-plan.json'), '{}')
|
|
56
|
-
const when = new Date(Date.now() - idleDays * DAY_MS)
|
|
57
|
-
utimesSync(join(dir, 'task-plan.json'), when, when)
|
|
58
|
-
return dir
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const cli = (...args) => spawnSync(process.execPath, [script, ...args], { encoding: 'utf8' })
|
|
62
|
-
const has = (dir, rel) => existsSync(join(dir, rel))
|
|
63
|
-
|
|
64
|
-
test('an idle run keeps records and logs, and loses downloads, caches, and clean source', () => {
|
|
65
|
-
const { remote, repo, rush } = fixture()
|
|
66
|
-
const run = makeRun(rush, 'old', 10)
|
|
67
|
-
write(join(run, 'exploration', 'findings.json'), '{}')
|
|
68
|
-
write(join(run, 'reviews', '1131-r1', 'review.md'), 'ok')
|
|
69
|
-
write(join(run, 'reviews', '1131-r1', 'sandbox', 'sdk', 'system.img'), 'big')
|
|
70
|
-
write(join(run, 'reviews', '1131-r1', 'sandbox', 'worker.log'), 'review log')
|
|
71
|
-
write(join(run, 'diagnoses', '1136', 'deep', 'nested', 'adb.txt'), 'adb not ready')
|
|
72
|
-
write(join(run, '.tmp', 'resume-result.json'), '{}')
|
|
73
|
-
write(join(run, 'jdk17.tar.gz'), 'big')
|
|
74
|
-
mkdirSync(join(run, 'units'), { recursive: true })
|
|
75
|
-
const ws = clone(remote, join(run, 'units', '1132'))
|
|
76
|
-
|
|
77
|
-
const result = cli('--repo', repo, '--apply')
|
|
78
|
-
assert.equal(result.status, 0, result.stderr)
|
|
79
|
-
|
|
80
|
-
for (const kept of ['task-plan.json', 'exploration/findings.json', 'reviews/1131-r1/review.md', 'reviews/1131-r1/sandbox/worker.log', 'diagnoses/1136/deep/nested/adb.txt', '.tmp/resume-result.json']) {
|
|
81
|
-
assert.ok(has(run, kept), `${kept} should be kept`)
|
|
82
|
-
}
|
|
83
|
-
for (const gone of ['reviews/1131-r1/sandbox/sdk', 'jdk17.tar.gz']) assert.ok(!has(run, gone), `${gone} should be removed`)
|
|
84
|
-
|
|
85
|
-
// The clean clone keeps only its logs: no source, .git, installs, caches, or emulator disks.
|
|
86
|
-
assert.ok(has(ws, 'test-results/journey.log'))
|
|
87
|
-
for (const gone of ['app.js', 'docs', '.git', '.gitignore', 'node_modules', '.cache', 'test-results/avd']) assert.ok(!has(ws, gone), `clone ${gone} should be removed`)
|
|
88
|
-
assert.deepEqual(readdirSync(ws), ['test-results'])
|
|
89
|
-
assert.match(result.stdout, /sweep\s+units\/1132 \(clean and pushed; keeps 1 log file\(s\)/)
|
|
90
|
-
assert.match(result.stdout.trim().split('\n').at(-1), /^cleanup-runs: 1 idle run\(s\), 0 active; .*1 clean clone\(s\) emptied.*log file\(s\) kept; freed/)
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
test('a clone with unpushed commits, edits, or a stash keeps its work and logs, and loses ignored downloads', () => {
|
|
94
|
-
const { remote, repo, rush } = fixture()
|
|
95
|
-
const run = makeRun(rush, 'old', 10)
|
|
96
|
-
mkdirSync(join(run, 'units'), { recursive: true })
|
|
97
|
-
|
|
98
|
-
const committed = clone(remote, join(run, 'units', 'committed'))
|
|
99
|
-
write(join(committed, 'app.js'), 'v2')
|
|
100
|
-
git(committed, 'commit', '--quiet', '-am', 'local only')
|
|
101
|
-
|
|
102
|
-
const edited = clone(remote, join(run, 'units', 'edited'))
|
|
103
|
-
write(join(edited, 'new.js'), 'untracked work')
|
|
104
|
-
|
|
105
|
-
const stashed = clone(remote, join(run, 'units', 'stashed'))
|
|
106
|
-
write(join(stashed, 'app.js'), 'stashed work')
|
|
107
|
-
git(stashed, 'stash', '--quiet')
|
|
108
|
-
|
|
109
|
-
const result = cli('--repo', repo, '--apply')
|
|
110
|
-
assert.equal(result.status, 0, result.stderr)
|
|
111
|
-
|
|
112
|
-
assert.equal(git(committed, 'rev-list', '--count', 'HEAD', '--not', '--remotes').trim(), '1')
|
|
113
|
-
assert.ok(has(edited, 'new.js'))
|
|
114
|
-
assert.equal(git(stashed, 'stash', 'list').split('\n').filter(Boolean).length, 1)
|
|
115
|
-
for (const dir of [committed, edited, stashed]) {
|
|
116
|
-
assert.ok(has(dir, 'app.js') && has(dir, 'docs/README.md'), `${dir} keeps tracked files`)
|
|
117
|
-
assert.ok(has(dir, 'test-results/journey.log'), `${dir} keeps its log`)
|
|
118
|
-
for (const gone of ['node_modules', '.cache', 'test-results/avd']) assert.ok(!has(dir, gone), `${dir} ${gone} should be stripped`)
|
|
119
|
-
}
|
|
120
|
-
assert.match(result.stdout, /strip\s+units\/committed \(keeps 1 unpushed commit\(s\) and 1 log file\(s\)/)
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
test('past the log window only records remain; clones with work still keep their work', () => {
|
|
124
|
-
const { remote, repo, rush } = fixture()
|
|
125
|
-
const run = makeRun(rush, 'ancient', 40)
|
|
126
|
-
write(join(run, 'reviews', '1131-r1.json'), '{}')
|
|
127
|
-
write(join(run, 'reviews', '1131-r1', 'sandbox', 'worker.log'), 'old log')
|
|
128
|
-
mkdirSync(join(run, 'units'), { recursive: true })
|
|
129
|
-
const clean = clone(remote, join(run, 'units', 'clean'))
|
|
130
|
-
const busy = clone(remote, join(run, 'units', 'busy'))
|
|
131
|
-
write(join(busy, 'app.js'), 'v2')
|
|
132
|
-
git(busy, 'commit', '--quiet', '-am', 'local only')
|
|
133
|
-
|
|
134
|
-
const result = cli('--repo', repo, '--log-days', '30', '--apply')
|
|
135
|
-
assert.equal(result.status, 0, result.stderr)
|
|
136
|
-
assert.match(result.stdout, /records only, past 30 days/)
|
|
137
|
-
assert.ok(has(run, 'reviews/1131-r1.json'))
|
|
138
|
-
assert.ok(!has(run, 'reviews/1131-r1'), 'the old log and its folder go')
|
|
139
|
-
assert.ok(!existsSync(clean), 'a clean clone with no logs left is deleted')
|
|
140
|
-
assert.ok(has(busy, 'app.js'))
|
|
141
|
-
assert.ok(!has(busy, 'test-results'), 'its ignored log goes too')
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
test('text inside a tool cache is not evidence, and an oversized log is dropped', () => {
|
|
145
|
-
const { repo, rush } = fixture()
|
|
146
|
-
const run = makeRun(rush, 'old', 10)
|
|
147
|
-
write(join(run, 'home', '.npm', '_cacache', 'index.json'), '{}')
|
|
148
|
-
write(join(run, 'xdg-cache', 'pnpm', 'metadata.jsonl'), '{}')
|
|
149
|
-
write(join(run, 'builds', 'Pixel.avd', 'config.txt'), 'x')
|
|
150
|
-
write(join(run, 'logs', 'huge.log'), Buffer.alloc(EVIDENCE_MAX_BYTES + 1))
|
|
151
|
-
write(join(run, 'logs', 'small.log'), 'ok')
|
|
152
|
-
|
|
153
|
-
assert.equal(cli('--repo', repo, '--apply').status, 0)
|
|
154
|
-
for (const gone of ['home', 'xdg-cache', 'builds', 'logs/huge.log']) assert.ok(!has(run, gone), `${gone} should be removed`)
|
|
155
|
-
assert.ok(has(run, 'logs/small.log'))
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
test('a clone hidden inside a dot directory is still protected', () => {
|
|
159
|
-
const { remote, repo, rush } = fixture()
|
|
160
|
-
const run = makeRun(rush, 'old', 10)
|
|
161
|
-
mkdirSync(join(run, '.tmp'), { recursive: true })
|
|
162
|
-
const hidden = clone(remote, join(run, '.tmp', 'wip'))
|
|
163
|
-
write(join(hidden, 'app.js'), 'v2')
|
|
164
|
-
git(hidden, 'commit', '--quiet', '-am', 'local only')
|
|
165
|
-
|
|
166
|
-
assert.equal(cli('--repo', repo, '--apply').status, 0)
|
|
167
|
-
assert.ok(has(hidden, 'app.js'))
|
|
168
|
-
assert.ok(!has(hidden, 'node_modules'))
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
test('a run used recently is left alone, and a dry run removes nothing', () => {
|
|
172
|
-
const { remote, repo, rush } = fixture()
|
|
173
|
-
const active = makeRun(rush, 'active', 1)
|
|
174
|
-
write(join(active, 'units', 'scratch', 'big.img'), 'big')
|
|
175
|
-
const idle = makeRun(rush, 'idle', 10)
|
|
176
|
-
mkdirSync(join(idle, 'units'), { recursive: true })
|
|
177
|
-
clone(remote, join(idle, 'units', 'a'))
|
|
178
|
-
|
|
179
|
-
const dry = cli('--repo', repo)
|
|
180
|
-
assert.equal(dry.status, 0, dry.stderr)
|
|
181
|
-
assert.ok(has(idle, 'units/a/node_modules'))
|
|
182
|
-
assert.match(dry.stdout.trim().split('\n').at(-1), /dry run, nothing removed.*Rerun with --apply/)
|
|
183
|
-
|
|
184
|
-
assert.equal(cli('--repo', repo, '--older-than-days', '3', '--apply').status, 0)
|
|
185
|
-
assert.ok(has(active, 'units/scratch/big.img'))
|
|
186
|
-
assert.ok(!has(idle, 'units/a/node_modules'))
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
test('runs are staged by task-plan.json age', () => {
|
|
190
|
-
const { repo, rush } = fixture()
|
|
191
|
-
makeRun(rush, 'a-two', 2)
|
|
192
|
-
makeRun(rush, 'b-ten', 10)
|
|
193
|
-
makeRun(rush, 'c-forty', 40)
|
|
194
|
-
const stages = planSweep({ repo, olderThanDays: 3, logDays: 30 }).runs.map((r) => [r.run, r.stage])
|
|
195
|
-
assert.deepEqual(stages, [['a-two', 'active'], ['b-ten', 'keep-logs'], ['c-forty', 'records-only']])
|
|
196
|
-
assert.deepEqual(planSweep({ repo: join(repo, 'missing') }).runs, [])
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
test('--merged empties a merged clean workspace to its logs, even with commits that look unpushed', () => {
|
|
200
|
-
const { remote, rush } = fixture()
|
|
201
|
-
const run = makeRun(rush, 'now', 0)
|
|
202
|
-
mkdirSync(join(run, 'units'), { recursive: true })
|
|
203
|
-
const ws = clone(remote, join(run, 'units', 'merged'))
|
|
204
|
-
write(join(ws, 'app.js'), 'v2')
|
|
205
|
-
git(ws, 'commit', '--quiet', '-am', 'squash-merged upstream')
|
|
206
|
-
|
|
207
|
-
const result = cli('--merged', ws, '--apply')
|
|
208
|
-
assert.equal(result.status, 0, result.stderr)
|
|
209
|
-
assert.deepEqual(readdirSync(ws), ['test-results'])
|
|
210
|
-
assert.ok(has(ws, 'test-results/journey.log'))
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
test('--merged keeps uncommitted edits and refuses paths outside .resolve-issues', () => {
|
|
214
|
-
const { base, remote, rush } = fixture()
|
|
215
|
-
const run = makeRun(rush, 'now', 0)
|
|
216
|
-
mkdirSync(join(run, 'units'), { recursive: true })
|
|
217
|
-
const ws = clone(remote, join(run, 'units', 'dirty'))
|
|
218
|
-
write(join(ws, 'app.js'), 'uncommitted')
|
|
219
|
-
|
|
220
|
-
assert.equal(cli('--merged', ws, '--apply').status, 0)
|
|
221
|
-
assert.ok(has(ws, 'app.js') && has(ws, 'test-results/journey.log'))
|
|
222
|
-
assert.ok(!has(ws, 'node_modules'))
|
|
223
|
-
|
|
224
|
-
const outside = clone(remote, join(base, 'elsewhere'))
|
|
225
|
-
const refused = cli('--merged', outside, '--apply')
|
|
226
|
-
assert.equal(refused.status, 2)
|
|
227
|
-
assert.match(refused.stderr, /only removes workspaces under a \.resolve-issues directory/)
|
|
228
|
-
assert.ok(existsSync(outside))
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
test('a bad command exits 2 with usage', () => {
|
|
232
|
-
for (const args of [['--older-than-days', 'soon'], ['--bogus'], ['--repo'], ['--log-days', '-1']]) {
|
|
233
|
-
const result = cli(...args)
|
|
234
|
-
assert.equal(result.status, 2)
|
|
235
|
-
assert.match(result.stderr, /^usage: cleanup-runs\.mjs/)
|
|
236
|
-
}
|
|
237
|
-
const inverted = cli('--older-than-days', '10', '--log-days', '5')
|
|
238
|
-
assert.equal(inverted.status, 2)
|
|
239
|
-
assert.match(inverted.stderr, /--log-days must be at least --older-than-days/)
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
test('in a non-Git copy of a repository, only logs and text under evidence folders are kept', () => {
|
|
243
|
-
const { repo, rush } = fixture()
|
|
244
|
-
const run = makeRun(rush, 'old', 10)
|
|
245
|
-
const copy = join(run, 'units', '1132', 'test-results', 'packet', 'build-x', 'workspace')
|
|
246
|
-
write(join(copy, 'pnpm-lock.yaml'), 'lock')
|
|
247
|
-
write(join(copy, 'package.json'), '{}')
|
|
248
|
-
write(join(copy, 'docs', 'design.md'), '# copied source doc')
|
|
249
|
-
write(join(copy, 'build.log'), 'gradle failed')
|
|
250
|
-
write(join(copy, 'test-results', 'summary.json'), '{}')
|
|
251
|
-
write(join(run, 'units', '1132', 'test-results', 'packet', 'gradle', 'caches', 'meta.json'), '{}')
|
|
252
|
-
write(join(run, 'units', '1132', 'test-results', 'packet', 'result.json'), '{}')
|
|
253
|
-
|
|
254
|
-
assert.equal(cli('--repo', repo, '--apply').status, 0)
|
|
255
|
-
for (const kept of ['build.log', 'test-results/summary.json']) assert.ok(has(copy, kept), `${kept} should be kept`)
|
|
256
|
-
for (const gone of ['package.json', 'pnpm-lock.yaml', 'docs']) assert.ok(!has(copy, gone), `${gone} should be removed`)
|
|
257
|
-
assert.ok(!has(run, 'units/1132/test-results/packet/gradle'))
|
|
258
|
-
assert.ok(has(run, 'units/1132/test-results/packet/result.json'))
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
test('read-only package stores are removed, not left to abort the run', () => {
|
|
262
|
-
const { repo, rush } = fixture()
|
|
263
|
-
const run = makeRun(rush, 'old', 10)
|
|
264
|
-
const store = join(run, 'artifacts', 'pnpm-v11', 'v11')
|
|
265
|
-
write(join(store, 'files', '61', 'abc'), 'blob')
|
|
266
|
-
write(join(store, 'index.db'), 'db')
|
|
267
|
-
for (const p of [join(store, 'files', '61', 'abc'), join(store, 'index.db')]) chmodSync(p, 0o444)
|
|
268
|
-
write(join(run, 'artifacts', 'pnpm-tuple', 'store', 'blob'), 'x')
|
|
269
|
-
write(join(run, 'artifacts', 'pnpm-tuple', 'install.log'), 'kept log')
|
|
270
|
-
for (const d of [join(store, 'files', '61'), join(store, 'files'), store, join(run, 'artifacts', 'pnpm-tuple', 'store'), join(run, 'artifacts', 'pnpm-tuple')]) chmodSync(d, 0o555)
|
|
271
|
-
write(join(run, 'zz', 'worker.log'), 'kept')
|
|
272
|
-
|
|
273
|
-
const result = cli('--repo', repo, '--apply')
|
|
274
|
-
assert.equal(result.status, 0, result.stderr)
|
|
275
|
-
assert.ok(!has(run, 'artifacts/pnpm-v11'), 'the read-only store is gone')
|
|
276
|
-
assert.ok(!has(run, 'artifacts/pnpm-tuple/store'), 'a store inside a read-only folder is gone')
|
|
277
|
-
assert.ok(has(run, 'artifacts/pnpm-tuple/install.log'), 'its sibling log stays')
|
|
278
|
-
assert.ok(has(run, 'zz/worker.log'), 'later paths are still processed')
|
|
279
|
-
assert.doesNotMatch(result.stdout, /could not be removed/)
|
|
280
|
-
})
|