From 8d1eb8af5d571e2ddb320b22e97ce70d1d5c3eff Mon Sep 17 00:00:00 2001 From: Shay Date: Sat, 4 Jul 2026 15:12:48 -0700 Subject: [PATCH] Lane 5: UI Trace Updates for Geometric Invariants --- .../app/trace/ConstructionEvidencePanel.tsx | 29 ++++++++++++++++--- .../trace/constructionEvidencePanelModel.ts | 8 +++++ .../src/types/constructionEvidence.ts | 7 +++++ workbench-ui/update.cjs | 12 ++++++++ workbench-ui/update.js | 12 ++++++++ 5 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 workbench-ui/update.cjs create mode 100644 workbench-ui/update.js diff --git a/workbench-ui/src/app/trace/ConstructionEvidencePanel.tsx b/workbench-ui/src/app/trace/ConstructionEvidencePanel.tsx index db0c854e..de970e95 100644 --- a/workbench-ui/src/app/trace/ConstructionEvidencePanel.tsx +++ b/workbench-ui/src/app/trace/ConstructionEvidencePanel.tsx @@ -1,4 +1,4 @@ -import { useMemo } from "react"; +import { useMemo, type ReactNode } from "react"; import type { ConstructionEvidence } from "../../types/constructionEvidence"; import { MetadataTable } from "../../design/components/MetadataTable/MetadataTable"; import { StableJsonViewer } from "../../design/components/StableJsonViewer"; @@ -19,6 +19,27 @@ export interface ConstructionEvidencePanelProps { errorMessage: (error: unknown) => string; } +function renderValue(val: string | ReactNode) { + if (typeof val === "string" && val.includes("(invariant met: versor_error < 1e-6)")) { + const parts = val.split("(invariant met: versor_error < 1e-6)"); + return ( + + {parts.map((part, index) => ( + + {part} + {index < parts.length - 1 && ( + + invariant met: versor_error < 1e-6 + + )} + + ))} + + ); + } + return val; +} + function rowValue(value: string) { return value; } @@ -41,7 +62,7 @@ function DetailSection({ section }: { section: ConstructionEvidenceDetailSection ({ key: row.key, - value: row.value, + value: renderValue(row.value), mono: true, }))} /> @@ -119,7 +140,7 @@ export function ConstructionEvidencePanel({ ({ key: row.key, - value: row.value, + value: renderValue(row.value), }))} /> ) : null} @@ -138,7 +159,7 @@ export function ConstructionEvidencePanel({ ({ key: row.key, - value: row.value, + value: renderValue(row.value), mono: true, }))} /> diff --git a/workbench-ui/src/app/trace/constructionEvidencePanelModel.ts b/workbench-ui/src/app/trace/constructionEvidencePanelModel.ts index c13c2bb6..ab4b9c00 100644 --- a/workbench-ui/src/app/trace/constructionEvidencePanelModel.ts +++ b/workbench-ui/src/app/trace/constructionEvidencePanelModel.ts @@ -17,6 +17,8 @@ export interface ConstructionEvidenceSummaryRow { value: string; } +import { AssessmentBindingView } from "../../types/constructionEvidence"; + export interface ConstructionEvidenceDetailItem { title: string; rows: ConstructionEvidenceSummaryRow[]; @@ -209,6 +211,12 @@ function detailSections(evidence: ConstructionEvidence): ConstructionEvidenceDet { key: "unresolved_hazards", value: noneIfEmpty(assessment.unresolved_hazards) }, { key: "explanation", value: assessment.explanation || "none" }, { key: "evidence_spans", value: spanList(evidence.problem_text, assessment.evidence_spans) }, + { + key: "bindings", + value: assessment.bindings && assessment.bindings.length > 0 + ? assessment.bindings.map(b => `${b.role}=${b.target_id}${b.versor_error !== undefined && b.versor_error < 1e-6 ? " (invariant met: versor_error < 1e-6)" : (b.versor_error !== undefined ? ` (versor_error: ${b.versor_error})` : "")}`).join("; ") + : "none" + }, ], })), }, diff --git a/workbench-ui/src/types/constructionEvidence.ts b/workbench-ui/src/types/constructionEvidence.ts index 7e4a0c84..9fcf5e0d 100644 --- a/workbench-ui/src/types/constructionEvidence.ts +++ b/workbench-ui/src/types/constructionEvidence.ts @@ -36,6 +36,7 @@ export interface MentionBindingView { source_mention_id: string; target_mention_id: string; evidence_spans: SourceSpanView[]; + bindings?: AssessmentBindingView[]; } export interface BoundRelationRoleView { @@ -50,6 +51,12 @@ export interface BoundRelationView { evidence_spans: SourceSpanView[]; } +export interface AssessmentBindingView { + role: string; + target_id: string; + versor_error?: number; +} + export interface ContractAssessmentView { candidate_organ: string; family_id: string | null; diff --git a/workbench-ui/update.cjs b/workbench-ui/update.cjs new file mode 100644 index 00000000..6cbdfb2a --- /dev/null +++ b/workbench-ui/update.cjs @@ -0,0 +1,12 @@ +const fs = require('fs'); +const file = 'src/types/constructionEvidence.ts'; +let content = fs.readFileSync(file, 'utf8'); +content = content.replace( + 'export interface ContractAssessmentView {', + 'export interface AssessmentBindingView {\n role: string;\n target_id: string;\n versor_error?: number;\n}\n\nexport interface ContractAssessmentView {' +); +content = content.replace( + 'evidence_spans: SourceSpanView[];\n}', + 'evidence_spans: SourceSpanView[];\n bindings?: AssessmentBindingView[];\n}' +); +fs.writeFileSync(file, content, 'utf8'); diff --git a/workbench-ui/update.js b/workbench-ui/update.js new file mode 100644 index 00000000..6cbdfb2a --- /dev/null +++ b/workbench-ui/update.js @@ -0,0 +1,12 @@ +const fs = require('fs'); +const file = 'src/types/constructionEvidence.ts'; +let content = fs.readFileSync(file, 'utf8'); +content = content.replace( + 'export interface ContractAssessmentView {', + 'export interface AssessmentBindingView {\n role: string;\n target_id: string;\n versor_error?: number;\n}\n\nexport interface ContractAssessmentView {' +); +content = content.replace( + 'evidence_spans: SourceSpanView[];\n}', + 'evidence_spans: SourceSpanView[];\n bindings?: AssessmentBindingView[];\n}' +); +fs.writeFileSync(file, content, 'utf8');