mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
The distribution's track and runners-up, the switches and the surface read in theme inks, so they hold on a light ground. The hero's sentence takes a size that tracks the width on phones, keeping "don't generate text." on one line at 375. The harness board says what it is: Kai and Jev on Decision Index v2. evidence_sufficient is evidence_enough, so the graph's label clears its value. Co-authored-by: Hanzo Dev <[email protected]>
121 lines
5.9 KiB
TypeScript
121 lines
5.9 KiB
TypeScript
/**
|
|
* The scripted scenario the /kai hero plays: one vehicle, one Decision Program,
|
|
* and a deterministic answer for every combination of evidence.
|
|
*
|
|
* NOTHING HERE IS MODEL OUTPUT, and the figure says so on its face. Every
|
|
* probability is written below by hand, so the same toggles always draw the same
|
|
* graph, and a reader is shown what the mechanism does rather than a number that
|
|
* looks measured. The page states the model is in training; a figure that
|
|
* computed its answers would be a claim the page cannot back.
|
|
*
|
|
* The arithmetic is the least that still behaves like the mechanism it stands
|
|
* for: a decision's confidence is what it has with every source, less the share
|
|
* each missing source carried. Confident decisions freeze on the first pass;
|
|
* uncertain and dependent ones are refined on the second; a decision whose
|
|
* evidence cannot carry it defers and names what it needs.
|
|
*/
|
|
|
|
/** An evidence stream. `toggle` is whether the reader can switch it off. */
|
|
export type Source = 'camera' | 'thermal' | 'radar' | 'can' | 'audio' | 'sysml' | 'sim' | 'docs'
|
|
|
|
/** The shape a stream's trace draws in its scope window. */
|
|
export type Wave = 'frames' | 'slow' | 'ping' | 'bits' | 'noise' | 'struct' | 'saw' | 'sparse'
|
|
|
|
export const SOURCES: readonly { id: Source; label: string; name: string; wave: Wave; toggle: boolean }[] = [
|
|
{ id: 'camera', label: 'camera', name: 'camera', wave: 'frames', toggle: true },
|
|
{ id: 'thermal', label: 'thermal', name: 'thermal', wave: 'slow', toggle: false },
|
|
{ id: 'radar', label: 'radar', name: 'radar', wave: 'ping', toggle: false },
|
|
{ id: 'can', label: 'can', name: 'CAN', wave: 'bits', toggle: true },
|
|
{ id: 'audio', label: 'audio', name: 'audio', wave: 'noise', toggle: true },
|
|
{ id: 'sysml', label: 'sysml', name: 'SysML', wave: 'struct', toggle: true },
|
|
{ id: 'sim', label: 'sim', name: 'simulation', wave: 'saw', toggle: true },
|
|
{ id: 'docs', label: 'docs', name: 'docs', wave: 'sparse', toggle: false },
|
|
]
|
|
|
|
/**
|
|
* A typed variable in the program. `full` is its confidence with every source
|
|
* present; `weights` is how much of that each source carries. `lag` is how far
|
|
* below `full` the first pass sits for a decision that depends on another one —
|
|
* it cannot be confident before its parent is.
|
|
*/
|
|
export type Decision = {
|
|
id: string
|
|
value: string
|
|
full: number
|
|
weights: Partial<Record<Source, number>>
|
|
lag?: number
|
|
after?: string
|
|
/** A predicate's other answer. Below `DEFER` it is what the evidence supports, not a deferral. */
|
|
otherwise?: string
|
|
}
|
|
|
|
/** The program, in the order the figure lists it: each dependent under its parent. */
|
|
export const DECISIONS: readonly Decision[] = [
|
|
{ id: 'fault', value: 'bearing', full: 0.91, weights: { can: 0.2, audio: 0.16, camera: 0.05, sysml: 0.03 } },
|
|
{ id: 'readiness', value: 'no-go', full: 0.96, after: 'fault', weights: { can: 0.12, camera: 0.06, sysml: 0.05, sim: 0.06 } },
|
|
{ id: 'action', value: 'inspect-bearing', full: 0.83, lag: 0.3, after: 'fault', weights: { can: 0.12, audio: 0.12 } },
|
|
{ id: 'next_test', value: 'spectrum', full: 0.88, lag: 0.3, after: 'fault', weights: { audio: 0.14, can: 0.1 } },
|
|
{ id: 'severity', value: 'critical', full: 0.86, weights: { can: 0.15, audio: 0.1, sim: 0.08 } },
|
|
{ id: 'human_review', value: 'yes', full: 0.99, after: 'severity', weights: {} },
|
|
{ id: 'subsystem', value: 'drivetrain', full: 0.94, weights: { sysml: 0.25, can: 0.1 } },
|
|
{ id: 'evidence_enough', value: 'true', otherwise: 'false', full: 0.81, weights: { can: 0.1, audio: 0.08, camera: 0.06, sysml: 0.05, sim: 0.05 } },
|
|
{ id: 'thermal_margin', value: 'low', full: 0.87, weights: { sim: 0.2, can: 0.06 } },
|
|
{ id: 'mission_range', value: 'met', full: 0.92, weights: { sim: 0.25, sysml: 0.15 } },
|
|
{ id: 'visible_damage', value: 'none', full: 0.89, weights: { camera: 0.4 } },
|
|
{ id: 'operator_report', value: 'grinding', full: 0.84, weights: { audio: 0.45 } },
|
|
]
|
|
|
|
/** A first-pass confidence at or above this freezes; below it, the node is refined. */
|
|
export const FREEZE = 0.8
|
|
/** A final confidence below this is not a decision. The node defers. */
|
|
export const DEFER = 0.5
|
|
|
|
export type On = Record<Source, boolean>
|
|
|
|
export const ALL: On = Object.fromEntries(SOURCES.map((s) => [s.id, true])) as On
|
|
|
|
const round = (p: number) => Math.round(Math.min(0.99, Math.max(0.05, p)) * 100) / 100
|
|
|
|
/** Confidence once the program has settled, given which sources are present. */
|
|
export function final(d: Decision, on: On): number {
|
|
let p = d.full
|
|
for (const [s, w] of Object.entries(d.weights) as [Source, number][]) if (!on[s]) p -= w
|
|
return round(p)
|
|
}
|
|
|
|
/** Confidence on the first pass: a dependent sits below its settled value. */
|
|
export function draft(d: Decision, on: On): number {
|
|
return round(final(d, on) - (d.lag ?? 0))
|
|
}
|
|
|
|
/** The source whose absence costs this decision the most — what a DEFER asks for. */
|
|
export function needs(d: Decision, on: On): Source | null {
|
|
let best: Source | null = null
|
|
let most = 0
|
|
for (const [s, w] of Object.entries(d.weights) as [Source, number][]) {
|
|
if (!on[s] && w > most) {
|
|
most = w
|
|
best = s
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
/** The decisions a source reaches, and every decision that depends on one of them. */
|
|
export function reach(source: Source): string[] {
|
|
const hit = new Set(DECISIONS.filter((d) => (d.weights[source] ?? 0) > 0).map((d) => d.id))
|
|
for (const d of DECISIONS) if (d.after && hit.has(d.after)) hit.add(d.id)
|
|
return DECISIONS.filter((d) => hit.has(d.id)).map((d) => d.id)
|
|
}
|
|
|
|
/** What the node says once settled: its value and confidence, or the evidence it defers for. */
|
|
export function answer(d: Decision, on: On): { value: string; p: number; defer: Source | null } {
|
|
const p = final(d, on)
|
|
if (p >= DEFER) return { value: d.value, p, defer: null }
|
|
if (d.otherwise) return { value: d.otherwise, p: round(1 - p), defer: null }
|
|
return { value: 'defer', p, defer: needs(d, on) }
|
|
}
|
|
|
|
/** `.91`, the way the copy writes a probability. */
|
|
export const prob = (p: number) => p.toFixed(2).replace(/^0/, '')
|