- Deterministic scoring.py (quant 60 / qual 40 / flags -15, profitability heaviest) - Per-company JSON ledger with forecast-target chaining deck N-1 -> N - Single-shot sandbox agent with guided-JSON fallback ladder (no tool loop) - Portfolio dashboard with sparklines, KPI hit rates, BDEF category bars - 48 unit tests green; endpoints smoke-tested; npm check+build green Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
117 lines
3.9 KiB
TypeScript
117 lines
3.9 KiB
TypeScript
import { sdk } from '../sdk'
|
|
import { configFile } from '../file-models/config'
|
|
|
|
const { InputSpec, Value, List } = sdk
|
|
|
|
const inputSpec = InputSpec.of({
|
|
graders: Value.list(
|
|
List.obj(
|
|
{
|
|
name: 'Grading Panel',
|
|
description:
|
|
'Who grades the decks — one entry per grader. Add as many as you like. ' +
|
|
'Each grader is a model from your catalog plus a PERSONA: the lens it ' +
|
|
'grades through, so the same deck gets scored from different angles ' +
|
|
'before the deterministic composite is computed.',
|
|
default: [],
|
|
minLength: 1,
|
|
maxLength: 32,
|
|
},
|
|
{
|
|
uniqueBy: 'name',
|
|
displayAs: '{{name}} ({{model}})',
|
|
spec: InputSpec.of({
|
|
name: Value.text({
|
|
name: 'Name',
|
|
description: 'Unique grader name. Becomes its container and grade-sheet filename.',
|
|
required: true,
|
|
default: null,
|
|
placeholder: 'munger-lens',
|
|
patterns: [
|
|
{ regex: '^[A-Za-z0-9][A-Za-z0-9 _-]{0,40}$',
|
|
description: 'Letters, numbers, spaces, dashes, underscores (max 41 chars).' },
|
|
],
|
|
}),
|
|
model: Value.text({
|
|
name: 'Model Alias',
|
|
description: 'Which catalog model this grader uses (must match an alias from "Configure Models").',
|
|
required: true,
|
|
default: null,
|
|
placeholder: 'grader-a',
|
|
}),
|
|
persona: Value.textarea({
|
|
name: 'Persona / Lens',
|
|
description:
|
|
'How THIS grader should read the deck — its priorities and ' +
|
|
'weighting within the BDEF rubric. Injected into its system prompt. ' +
|
|
'e.g. "You are a Munger-style inversion skeptic: ask what would have ' +
|
|
'to be true for this deck to be hiding a deteriorating business." ' +
|
|
'Leave empty for a neutral grader.',
|
|
required: false,
|
|
default: null,
|
|
minRows: 3,
|
|
maxRows: 16,
|
|
placeholder:
|
|
'You are a Girdley-style operator. Weight unit economics, owner ' +
|
|
'accountability, and whether the KPIs the board was promised last ' +
|
|
'quarter are still being reported. Flag every silently dropped metric.',
|
|
}),
|
|
temperature: Value.number({
|
|
name: 'Sampling Temperature (optional)',
|
|
description:
|
|
'Best-effort per-grader sampling temperature for extra diversity. ' +
|
|
'Persona is the primary lever. Leave empty to use the model default.',
|
|
required: false,
|
|
default: null,
|
|
integer: false,
|
|
min: 0,
|
|
max: 2,
|
|
}),
|
|
}),
|
|
},
|
|
),
|
|
),
|
|
})
|
|
|
|
export const configureGraders = sdk.Action.withInput(
|
|
'configure-graders',
|
|
|
|
async ({ effects }) => ({
|
|
name: 'Configure Graders',
|
|
description: 'Define the grading panel: which models and which personas grade each deck.',
|
|
warning: null,
|
|
allowedStatuses: 'any',
|
|
group: 'Grading',
|
|
visibility: 'enabled',
|
|
}),
|
|
|
|
inputSpec,
|
|
|
|
async ({ effects }) => {
|
|
const cfg = await configFile.read().const(effects)
|
|
if (!cfg) return {}
|
|
return { graders: cfg.graders }
|
|
},
|
|
|
|
async ({ effects, input }) => {
|
|
await configFile.merge(effects, { graders: input.graders })
|
|
|
|
return {
|
|
version: '1',
|
|
title: 'Panel Configured',
|
|
message:
|
|
'Saved a panel of ' + input.graders.length + ' grader(s). Each model ' +
|
|
'alias must exist in "Configure Models". Set the scoring knobs in ' +
|
|
'"Configure Grading", add companies in "Configure Companies", then drop ' +
|
|
'decks into inbox/<company-slug>/ and run "Grade Decks".',
|
|
result: {
|
|
type: 'single',
|
|
value: input.graders.map((g) => g.name).join(', '),
|
|
copyable: false,
|
|
qr: false,
|
|
masked: false,
|
|
},
|
|
}
|
|
},
|
|
)
|