Skip to content

Commit 6bb3885

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/github-issue-11-9hCO5
# Conflicts: # src/server/preflight.ts
2 parents 5927f1c + fa92a23 commit 6bb3885

11 files changed

Lines changed: 65 additions & 38 deletions

File tree

.github/FUNDING.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# These are supported funding model platforms
2+
3+
github: [slaFFik]
4+
patreon: # Replace with a single Patreon username
5+
open_collective: # Replace with a single Open Collective username
6+
ko_fi: # Replace with a single Ko-fi username
7+
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8+
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9+
liberapay: # Replace with a single Liberapay username
10+
issuehunt: # Replace with a single IssueHunt username
11+
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12+
polar: # Replace with a single Polar username
13+
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
14+
thanks_dev: # Replace with a single thanks.dev username
15+
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

.github/workflows/ci.yml

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -50,59 +50,44 @@ jobs:
5050
- uses: actions/checkout@v6
5151
- uses: ./.github/actions/setup
5252
with:
53-
# ESLint v10 requires ^20.19.0 || ^22.13.0 || >=24, so pick a Node
54-
# that satisfies it (the typecheck/build/test matrix stays on the
55-
# project's engines floor 20.19/22.12 to test the actual runtime).
5653
node-version: '22.13.x'
5754
- run: npm run lint
5855
- run: npm run format:check
5956

6057
typecheck:
61-
name: Typecheck (Node ${{ matrix.node-version }})
58+
name: Typecheck
6259
runs-on: ubuntu-latest
6360
timeout-minutes: 10
6461
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
65-
strategy:
66-
fail-fast: false
67-
matrix:
68-
node-version: ['20.19.x', '22.12.x']
6962
steps:
7063
- uses: actions/checkout@v6
7164
- uses: ./.github/actions/setup
7265
with:
73-
node-version: ${{ matrix.node-version }}
66+
node-version: '22.13.x'
7467
- run: npm run typecheck
7568

7669
build:
77-
name: Build (Node ${{ matrix.node-version }})
70+
name: Build
7871
runs-on: ubuntu-latest
7972
timeout-minutes: 10
8073
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
81-
strategy:
82-
fail-fast: false
83-
matrix:
84-
node-version: ['20.19.x', '22.12.x']
8574
steps:
8675
- uses: actions/checkout@v6
8776
- uses: ./.github/actions/setup
8877
with:
89-
node-version: ${{ matrix.node-version }}
78+
node-version: '22.13.x'
9079
- run: npm run build
9180

9281
test:
93-
name: Test (Node ${{ matrix.node-version }})
82+
name: Test
9483
runs-on: ubuntu-latest
9584
timeout-minutes: 15
9685
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
97-
strategy:
98-
fail-fast: false
99-
matrix:
100-
node-version: ['20.19.x', '22.12.x']
10186
steps:
10287
- uses: actions/checkout@v6
10388
- uses: ./.github/actions/setup
10489
with:
105-
node-version: ${{ matrix.node-version }}
90+
node-version: '22.13.x'
10691
- run: npm run test:preflight
10792
- run: npm run test:judge
10893
- run: npm run test:runner

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
22

package-lock.json

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"test:sandbox": "tsx test/sandbox.spec.ts"
3030
},
3131
"engines": {
32-
"node": "^20.19.0 || >=22.12.0"
32+
"node": ">=22.13.0"
3333
},
3434
"dependencies": {
3535
"@radix-ui/react-tooltip": "^1.2.8",
@@ -42,7 +42,7 @@
4242
},
4343
"devDependencies": {
4444
"@eslint/js": "^10.0.1",
45-
"@types/node": "^20.19.39",
45+
"@types/node": "^22.19.17",
4646
"@types/proper-lockfile": "^4.1.4",
4747
"@types/react": "^19.2.14",
4848
"@types/react-dom": "^19.2.3",

src/server/preflight.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { promisify } from 'node:util';
33
import { homedir, tmpdir } from 'node:os';
44
import { join } from 'node:path';
55
import { pathExists, atomicWriteFile, ensureDir, readJsonIfExists } from './fsUtil.js';
6-
import { PROJECT_MARKERS, STORAGE_DIR_NAME } from '@shared/constants.js';
6+
import { JUDGE_MODEL, PROJECT_MARKERS, STORAGE_DIR_NAME } from '@shared/constants.js';
77
import { readdir, readFile, stat, unlink, writeFile } from 'node:fs/promises';
88
import lockfile from 'proper-lockfile';
99
import { log } from './log.js';
@@ -114,7 +114,7 @@ export async function authSmokeTest(bin: string): Promise<void> {
114114
'-p',
115115
'ping',
116116
'--model',
117-
'haiku',
117+
JUDGE_MODEL,
118118
'--output-format',
119119
'json',
120120
'--tools',

src/server/runner.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,10 @@ export class Runner extends EventEmitter {
382382
},
383383
);
384384

385+
if (typeof raw.model === 'string' && raw.model.length > 0) {
386+
this.input.initialConfig.resolvedModel = raw.model;
387+
}
388+
385389
const memoryPaths = raw.memory_paths as Record<string, unknown> | undefined;
386390
const auto = memoryPaths?.auto;
387391
if (typeof auto !== 'string' || auto.length === 0) return;

src/server/slug.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { spawn } from 'node:child_process';
33
import { pathExists } from './fsUtil.js';
44
import { join } from 'node:path';
55
import { readdir } from 'node:fs/promises';
6+
import { SLUG_MODEL } from '@shared/constants.js';
67
import { log } from './log.js';
78

89
const MAX_SLUG_LENGTH = 32;
@@ -75,9 +76,9 @@ export async function deriveSlug(
7576
}
7677

7778
if (!slugBase) {
78-
const haikuSlug = await tryHaikuSlug(input.variantContent, input.claudeBin);
79-
if (haikuSlug && isSafeSlugFragment(haikuSlug)) {
80-
slugBase = haikuSlug;
79+
const modelSlug = await tryModelSlug(input.variantContent, input.claudeBin);
80+
if (modelSlug && isSafeSlugFragment(modelSlug)) {
81+
slugBase = modelSlug;
8182
source = 'haiku';
8283
}
8384
}
@@ -110,7 +111,7 @@ export function slugStoragePath(storageRoot: string, folderName: string): string
110111
return join(storageRoot, folderName);
111112
}
112113

113-
async function tryHaikuSlug(content: string, claudeBin: string): Promise<string | null> {
114+
async function tryModelSlug(content: string, claudeBin: string): Promise<string | null> {
114115
const prompt =
115116
'Produce a 2-4 word kebab-case slug summarizing this variant. Output only the slug, no quotes, no explanation. Example: "concise-style" or "verbose-debugging". Variant content follows:\n\n' +
116117
content.slice(0, 4_000);
@@ -129,7 +130,7 @@ async function tryHaikuSlug(content: string, claudeBin: string): Promise<string
129130
'-p',
130131
prompt,
131132
'--model',
132-
'haiku',
133+
SLUG_MODEL,
133134
'--output-format',
134135
'json',
135136
'--tools',
@@ -147,7 +148,7 @@ async function tryHaikuSlug(content: string, claudeBin: string): Promise<string
147148
let buf = '';
148149
proc.stdout.on('data', (d) => (buf += d.toString()));
149150
proc.stderr.on('data', (d) => {
150-
log.debug('haiku-slug stderr', { text: d.toString().slice(0, 200) });
151+
log.debug('model-slug stderr', { text: d.toString().slice(0, 200) });
151152
});
152153

153154
const timer = setTimeout(() => {
@@ -157,7 +158,7 @@ async function tryHaikuSlug(content: string, claudeBin: string): Promise<string
157158

158159
proc.on('error', (err) => {
159160
clearTimeout(timer);
160-
log.warn('haiku-slug spawn error', { error: err.message });
161+
log.warn('model-slug spawn error', { error: err.message });
161162
finish(null);
162163
});
163164

src/shared/constants.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,17 @@ export const PROJECT_MARKERS = [
2020
'pyproject.toml',
2121
];
2222

23-
export const JUDGE_MODEL = 'haiku';
23+
// Pin to a concrete model ID, not the `haiku` CLI alias: aliases can be
24+
// repointed to a future generation or removed, which would silently shift
25+
// score baselines or break the judge entirely (issue #13). Bump manually
26+
// when a new Haiku ships and rebaselining is acceptable.
27+
export const JUDGE_MODEL = 'claude-haiku-4-5';
28+
29+
// Model used to auto-generate run-folder slugs from variant content. Kept
30+
// separate from JUDGE_MODEL so the slug generator can move independently —
31+
// slugs are non-critical (they fall back gracefully) and don't anchor any
32+
// historical comparison the way judge scores do.
33+
export const SLUG_MODEL = 'claude-haiku-4-5';
2434

2535
// Judge input budgets (plan § Judge flow step 2).
2636
export const JUDGE_PROMPT_CAP_BYTES = 4 * 1024;

src/shared/schemas/run.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ export const RunConfigSchema = z.object({
2626
promptSha256: z.string(),
2727
prompt: z.string(),
2828
model: ModelIdSchema,
29+
// Concrete model identifier the CLI reports in its `system_init` event.
30+
// `model` above is what the user selected (often an alias like `haiku`);
31+
// this captures what actually ran, so reruns across an alias bump remain
32+
// comparable. Optional/nullable for back-compat with pre-existing configs.
33+
resolvedModel: ModelIdSchema.nullable().optional(),
2934
mode: ModeSchema,
3035
status: RunStatusSchema,
3136
startedAt: z.string(),

0 commit comments

Comments
 (0)