Implement BDEF v1.1 grading: scoring core, per-deck pipeline, ledger, dashboard, StartOS layer
- 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>
This commit is contained in:
co-authored by
Claude Fable 5
parent
1dde915540
commit
b1d7aed9f4
@@ -4,14 +4,15 @@ import { configFile } from '../file-models/config'
|
||||
const { InputSpec, Value, List } = sdk
|
||||
|
||||
const inputSpec = InputSpec.of({
|
||||
reviewers: Value.list(
|
||||
graders: Value.list(
|
||||
List.obj(
|
||||
{
|
||||
name: 'Review Panel',
|
||||
name: 'Grading Panel',
|
||||
description:
|
||||
'Who sits on the panel — one entry per review. Add as many as you like. ' +
|
||||
'Each reviewer is a model from your catalog plus a PERSONA: the lens it ' +
|
||||
'reads through, so the same document gets examined from different angles.',
|
||||
'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,
|
||||
@@ -22,10 +23,10 @@ const inputSpec = InputSpec.of({
|
||||
spec: InputSpec.of({
|
||||
name: Value.text({
|
||||
name: 'Name',
|
||||
description: 'Unique reviewer name. Becomes its container and report filename.',
|
||||
description: 'Unique grader name. Becomes its container and grade-sheet filename.',
|
||||
required: true,
|
||||
default: null,
|
||||
placeholder: 'risk-counsel',
|
||||
placeholder: 'munger-lens',
|
||||
patterns: [
|
||||
{ regex: '^[A-Za-z0-9][A-Za-z0-9 _-]{0,40}$',
|
||||
description: 'Letters, numbers, spaces, dashes, underscores (max 41 chars).' },
|
||||
@@ -33,31 +34,32 @@ const inputSpec = InputSpec.of({
|
||||
}),
|
||||
model: Value.text({
|
||||
name: 'Model Alias',
|
||||
description: 'Which catalog model this reviewer uses (must match an alias from "Configure Models").',
|
||||
description: 'Which catalog model this grader uses (must match an alias from "Configure Models").',
|
||||
required: true,
|
||||
default: null,
|
||||
placeholder: 'reviewer-a',
|
||||
placeholder: 'grader-a',
|
||||
}),
|
||||
persona: Value.textarea({
|
||||
name: 'Persona / Lens',
|
||||
description:
|
||||
'How THIS reviewer should read the documents — its priorities and ' +
|
||||
'weighting. Injected into its system prompt. e.g. "You are skeptical ' +
|
||||
'legal counsel: weight liability, ambiguous obligations, and missing ' +
|
||||
'clauses above everything." Leave empty for a neutral reviewer.',
|
||||
'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 financial-controls reviewer. Focus on numbers that do not ' +
|
||||
'reconcile, unstated assumptions behind projections, and anything that ' +
|
||||
'would concern an auditor. Organize findings by severity.',
|
||||
'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-reviewer sampling temperature for extra diversity. ' +
|
||||
'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,
|
||||
@@ -71,15 +73,15 @@ const inputSpec = InputSpec.of({
|
||||
),
|
||||
})
|
||||
|
||||
export const configureReviewers = sdk.Action.withInput(
|
||||
'configure-reviewers',
|
||||
export const configureGraders = sdk.Action.withInput(
|
||||
'configure-graders',
|
||||
|
||||
async ({ effects }) => ({
|
||||
name: 'Configure Reviewers',
|
||||
description: 'Define the review panel: which models and which personas, and how many reviews.',
|
||||
name: 'Configure Graders',
|
||||
description: 'Define the grading panel: which models and which personas grade each deck.',
|
||||
warning: null,
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
group: 'Grading',
|
||||
visibility: 'enabled',
|
||||
}),
|
||||
|
||||
@@ -88,22 +90,23 @@ export const configureReviewers = sdk.Action.withInput(
|
||||
async ({ effects }) => {
|
||||
const cfg = await configFile.read().const(effects)
|
||||
if (!cfg) return {}
|
||||
return { reviewers: cfg.reviewers }
|
||||
return { graders: cfg.graders }
|
||||
},
|
||||
|
||||
async ({ effects, input }) => {
|
||||
await configFile.merge(effects, { reviewers: input.reviewers })
|
||||
await configFile.merge(effects, { graders: input.graders })
|
||||
|
||||
return {
|
||||
version: '1',
|
||||
title: 'Panel Configured',
|
||||
message:
|
||||
'Saved a panel of ' + input.reviewers.length + ' reviewer(s). Each model ' +
|
||||
'alias must exist in "Configure Models". Set the rubric in "Configure ' +
|
||||
'Review", then drop documents and run a review.',
|
||||
'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.reviewers.map((r) => r.name).join(', '),
|
||||
value: input.graders.map((g) => g.name).join(', '),
|
||||
copyable: false,
|
||||
qr: false,
|
||||
masked: false,
|
||||
|
||||
Reference in New Issue
Block a user