fix(desktop): usability wiring — rubric fixture ids + cell-jump listener + artifact seed + empty panel
+ docs/integration-final-check.md — 5-item integration gate PASS
This commit is contained in:
93
examples/desktop/src/renderer/artifact-board-seed.js
Normal file
93
examples/desktop/src/renderer/artifact-board-seed.js
Normal file
@@ -0,0 +1,93 @@
|
||||
// Auto-inlined artifact-board fixture. Renderer runs at file:// so we
|
||||
// inline docs/artifact-board-fixture.json here instead of relying on fetch().
|
||||
// To refresh: node scripts/regen-artifact-board-seed.js
|
||||
|
||||
'use strict'
|
||||
;(function () {
|
||||
if (typeof window === 'undefined') return
|
||||
window.__dshArtifactBoardSeed = {
|
||||
"_note": "Fixture for lane-artifact-v2 (Artifacts evolution chain + Board view). Each entry is one artifact:event payload the renderer would receive. Multi-version entries share an artifactId; blob content is included so the evolution diff pane can render real per-hop line-diffs. The real ArtifactServer does not include content in its broadcast — that's the constraint the strip's fallback 'content not preserved' note is calibrated for.",
|
||||
"artifacts": [
|
||||
{
|
||||
"artifactId": "session.md",
|
||||
"kind": "md",
|
||||
"version": 1,
|
||||
"seenAt": 1747000000000,
|
||||
"path": "/w/.artifacts/session.md",
|
||||
"blob": "# Session log\n\nTurn 1: user asked to build a landing page.\n"
|
||||
},
|
||||
{
|
||||
"artifactId": "session.md",
|
||||
"kind": "md",
|
||||
"version": 2,
|
||||
"seenAt": 1747000060000,
|
||||
"path": "/w/.artifacts/session.md",
|
||||
"blob": "# Session log\n\nTurn 1: user asked to build a landing page.\nTurn 2: agent scaffolded landing.html and hero.svg.\n"
|
||||
},
|
||||
{
|
||||
"artifactId": "session.md",
|
||||
"kind": "md",
|
||||
"version": 3,
|
||||
"seenAt": 1747000180000,
|
||||
"path": "/w/.artifacts/session.md",
|
||||
"blob": "# Session log\n\nTurn 1: user asked to build a landing page.\nTurn 2: agent scaffolded landing.html and hero.svg.\nTurn 3: agent added responsive styles and OG tags.\nTurn 4: agent added README with build steps.\n"
|
||||
},
|
||||
{
|
||||
"artifactId": "landing.html",
|
||||
"kind": "html",
|
||||
"version": 1,
|
||||
"seenAt": 1747000030000,
|
||||
"path": "/w/.artifacts/landing.html",
|
||||
"blob": "<!doctype html>\n<html>\n<head><title>Landing</title></head>\n<body>\n <h1>Hello</h1>\n</body>\n</html>\n"
|
||||
},
|
||||
{
|
||||
"artifactId": "landing.html",
|
||||
"kind": "html",
|
||||
"version": 2,
|
||||
"seenAt": 1747000090000,
|
||||
"path": "/w/.artifacts/landing.html",
|
||||
"blob": "<!doctype html>\n<html>\n<head>\n <title>Landing</title>\n <meta name=\"description\" content=\"A demo landing page\">\n</head>\n<body>\n <h1>Hello</h1>\n <p>Welcome to our product.</p>\n</body>\n</html>\n"
|
||||
},
|
||||
{
|
||||
"artifactId": "hero.svg",
|
||||
"kind": "svg",
|
||||
"version": 1,
|
||||
"seenAt": 1747000045000,
|
||||
"path": "/w/.artifacts/hero.svg",
|
||||
"blob": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 200 100\">\n <rect width=\"200\" height=\"100\" fill=\"#eef\"/>\n</svg>\n"
|
||||
},
|
||||
{
|
||||
"artifactId": "config.json",
|
||||
"kind": "json",
|
||||
"version": 1,
|
||||
"seenAt": 1747000120000,
|
||||
"path": "/w/.artifacts/config.json",
|
||||
"blob": "{\n \"name\": \"landing\",\n \"version\": \"0.1.0\"\n}\n"
|
||||
},
|
||||
{
|
||||
"artifactId": "config.json",
|
||||
"kind": "json",
|
||||
"version": 2,
|
||||
"seenAt": 1747000200000,
|
||||
"path": "/w/.artifacts/config.json",
|
||||
"blob": "{\n \"name\": \"landing\",\n \"version\": \"0.2.0\",\n \"port\": 3000\n}\n"
|
||||
},
|
||||
{
|
||||
"artifactId": "build.sh",
|
||||
"kind": "sh",
|
||||
"version": 1,
|
||||
"seenAt": 1747000150000,
|
||||
"path": "/w/.artifacts/build.sh",
|
||||
"blob": "#!/bin/sh\nset -e\nnpm install\nnpm run build\n"
|
||||
},
|
||||
{
|
||||
"artifactId": "README.md",
|
||||
"kind": "md",
|
||||
"version": 1,
|
||||
"seenAt": 1747000210000,
|
||||
"path": "/w/.artifacts/README.md",
|
||||
"blob": "# Landing\n\nStatic landing page for the demo product.\n\n## Build\n\n```sh\nsh build.sh\n```\n"
|
||||
}
|
||||
]
|
||||
};
|
||||
})()
|
||||
@@ -292,6 +292,20 @@
|
||||
head.append(label, summary)
|
||||
el.append(head)
|
||||
|
||||
// Provenance banner: the real ArtifactServer broadcasts fs writes
|
||||
// with no blob content, so per-hop diffs collapse to the "content
|
||||
// not preserved" fallback in production. When any hop is missing
|
||||
// blob content we surface why up front rather than letting the
|
||||
// researcher assume the diff pane is broken. Fixture supplies
|
||||
// blobs so all hops render; production doesn't, yet.
|
||||
const missingBlobs = chain.some((c) => typeof c.blob !== 'string')
|
||||
if (missingBlobs) {
|
||||
const banner = document.createElement('div')
|
||||
banner.className = 'artifact-evolution-banner muted small'
|
||||
banner.textContent = 'Version diff currently requires blob payload. The real ArtifactServer does not preserve older blobs (RFC L-3 follow-up: snapshot store). The fixture supplies blobs for demo.'
|
||||
el.append(banner)
|
||||
}
|
||||
|
||||
const chainEl = document.createElement('ol')
|
||||
chainEl.className = 'artifact-evolution-chain'
|
||||
for (let i = 0; i < chain.length; i++) {
|
||||
|
||||
@@ -176,6 +176,12 @@
|
||||
b.panelEl = buildPanel()
|
||||
stream.appendChild(b.panelEl)
|
||||
}
|
||||
// Remove the cold-clone empty-state hint (if present) once we have
|
||||
// a real artifact to show. Introduced by lane-usability-fix so
|
||||
// seedBoardFixture's placeholder gets swept when a real artifact
|
||||
// event arrives.
|
||||
const emptyHint = b.panelEl.querySelector('[data-role="empty-state"]')
|
||||
if (emptyHint && emptyHint.parentNode) emptyHint.parentNode.removeChild(emptyHint)
|
||||
const groupHost = b.panelEl.querySelector('.artifact-group')
|
||||
groupHost.appendChild(el)
|
||||
}
|
||||
@@ -458,32 +464,87 @@
|
||||
setTimeout(() => el.classList.remove('artifact-flash'), 900)
|
||||
}
|
||||
|
||||
// Debug menu button — mocks a write into the artifact dir via IPC.
|
||||
// Debug menu button — seeds the board/timeline/evolution demo. The
|
||||
// real ArtifactServer broadcasts fs writes with no blob content, so
|
||||
// cold-clone researchers never see the Board/Timeline/Evolution
|
||||
// views' actual affordances. We route the multi-kind, multi-version
|
||||
// fixture through the same onArtifactEvent entry the real wire uses
|
||||
// so all three tabs light up via the production code path. When the
|
||||
// seed is unavailable we fall back to the original IPC single-file
|
||||
// mock.
|
||||
function bindMockButton() {
|
||||
const btn = document.getElementById('mock-artifact')
|
||||
if (!btn) return
|
||||
btn.addEventListener('click', async () => {
|
||||
btn.disabled = true
|
||||
try {
|
||||
await window.dsh.mockArtifact()
|
||||
const seed = typeof window !== 'undefined' ? window.__dshArtifactBoardSeed : null
|
||||
if (seed && Array.isArray(seed.artifacts) && seed.artifacts.length) {
|
||||
seedBoardFixture(seed.artifacts)
|
||||
} else if (window.dsh && typeof window.dsh.mockArtifact === 'function') {
|
||||
await window.dsh.mockArtifact()
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('mockArtifact failed', err)
|
||||
console.error('mockArtifact / seed failed', err)
|
||||
} finally {
|
||||
btn.disabled = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Feed the board fixture through the same onArtifactEvent path the
|
||||
// real ArtifactServer uses. `seenAt` is re-anchored to "moments ago"
|
||||
// so relative-time labels don't read as ~430d against the frozen
|
||||
// fixture timestamps. Idempotent: ensureCard() de-dups on artifactId,
|
||||
// recordHistory() de-dups on (artifactId, version).
|
||||
function seedBoardFixture(list) {
|
||||
if (!Array.isArray(list) || !list.length) return
|
||||
const base = Date.now() - list.length * 60_000
|
||||
list.forEach((raw, i) => {
|
||||
onArtifactEvent({ ...raw, seenAt: base + i * 60_000 })
|
||||
})
|
||||
}
|
||||
|
||||
// Cold-clone empty state: mount the panel scaffold on stream init so
|
||||
// researchers can see List/Board/Timeline tabs (and the "no artifacts
|
||||
// yet" hint) exist even before the first artifact:event lands. Until
|
||||
// now the panel only appeared after the first artifact — new users
|
||||
// saw a blank pane and had no idea the surface existed. Scoped to the
|
||||
// active session bucket so switching sessions before any artifact
|
||||
// fires still gets its own empty panel per session.
|
||||
function ensureEmptyPanel() {
|
||||
const s = streamEl()
|
||||
if (!s) return
|
||||
const b = bucket()
|
||||
if (b.panelEl && b.panelEl.isConnected) return
|
||||
b.panelEl = buildPanel()
|
||||
// Inject a muted empty-state hint into the List body. Removed on
|
||||
// first appendGrouped() call so real artifacts land in a clean host.
|
||||
const body = b.panelEl.querySelector('.artifact-panel-body')
|
||||
if (body) {
|
||||
const hint = document.createElement('div')
|
||||
hint.className = 'artifact-panel-empty muted small'
|
||||
hint.dataset.role = 'empty-state'
|
||||
hint.textContent = 'No artifacts yet — try “artifact” in the Debug menu, or run a session that writes to .artifacts/.'
|
||||
body.appendChild(hint)
|
||||
}
|
||||
s.appendChild(b.panelEl)
|
||||
}
|
||||
|
||||
// Wire up once the DOM + preload bridge are ready. The renderer script
|
||||
// tag is loaded after this one, so we just register the listener
|
||||
// eagerly.
|
||||
if (window.dsh && typeof window.dsh.onArtifact === 'function') {
|
||||
window.dsh.onArtifact(onArtifactEvent)
|
||||
}
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', bindMockButton)
|
||||
} else {
|
||||
function initDom() {
|
||||
bindMockButton()
|
||||
ensureEmptyPanel()
|
||||
}
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initDom)
|
||||
} else {
|
||||
initDom()
|
||||
}
|
||||
|
||||
// Route artifact events on a session id if the payload carries one.
|
||||
@@ -510,6 +571,8 @@
|
||||
cards,
|
||||
history,
|
||||
switchView,
|
||||
seedBoardFixture,
|
||||
ensureEmptyPanel,
|
||||
getView: () => bucket().currentView,
|
||||
setActiveSession,
|
||||
getActiveSessionId: () => activeSessionId,
|
||||
|
||||
@@ -169,9 +169,20 @@ function renderSessionGraph(container, snapshot) {
|
||||
label.setAttribute('y', String(pos.y + 4))
|
||||
label.textContent = node.label
|
||||
g.appendChild(label)
|
||||
if (typeof snapshot?.onSelect === 'function' && node.turnId) {
|
||||
// Bind click on any node that carries an actionable identifier:
|
||||
// turn nodes → turnId, fork nodes → childSessionId, user nodes →
|
||||
// seq. Only these get the pointer cursor; nodes without a target
|
||||
// fall back to the default cursor so the affordance matches
|
||||
// reality.
|
||||
const actionable =
|
||||
(node.turnId) ||
|
||||
(node.kind === 'fork' && node.childSessionId) ||
|
||||
(node.kind === 'user' && node.seq)
|
||||
if (typeof snapshot?.onSelect === 'function' && actionable) {
|
||||
g.addEventListener('click', () => snapshot.onSelect(node))
|
||||
g.style && (g.style.cursor = 'pointer')
|
||||
} else if (g.style) {
|
||||
g.style.cursor = 'default'
|
||||
}
|
||||
svg.appendChild(g)
|
||||
}
|
||||
|
||||
@@ -1369,6 +1369,11 @@
|
||||
Evolution renderers) and MUST load before artifacts.js so the L0 row
|
||||
version-chip click handler + view switcher find the module ready. -->
|
||||
<script src="./artifacts-board.js"></script>
|
||||
<!-- Board fixture (multi-version + multi-kind demo data). Consumed by
|
||||
the debug-menu "seed artifacts" seam so external researchers can
|
||||
reach the Board/Timeline/Evolution views without waiting for a
|
||||
real run to emit fs artifacts. -->
|
||||
<script src="./artifact-board-seed.js"></script>
|
||||
<script src="./artifacts.js"></script>
|
||||
<script src="./plugin-runtime-fold.js"></script>
|
||||
<script src="./mcp-tool-name.js"></script>
|
||||
|
||||
@@ -4291,13 +4291,29 @@ function refreshChatSideDrawer() {
|
||||
sessionId: state.activeSessionId || '',
|
||||
model: meta && (meta.model || (meta.header && meta.header.model)) || '',
|
||||
events,
|
||||
selectedTurnId: null,
|
||||
selectedTurnId: state.chatSelectedTurnId || null,
|
||||
onSelect(row) {
|
||||
if (!row || !row.turnId) return
|
||||
if (!row) return
|
||||
// User rows have no turnId — scroll to the matching data-seq
|
||||
// anchor instead of the no-op early return. Cursor:pointer on
|
||||
// the whole row promises interactivity; without this the row
|
||||
// reads as a dead affordance.
|
||||
if (row.kind === 'user' && row.seq) {
|
||||
const seqTarget = streamEl && streamEl.querySelector(`[data-seq="${row.seq}"]`)
|
||||
if (seqTarget && typeof seqTarget.scrollIntoView === 'function') {
|
||||
seqTarget.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||
}
|
||||
return
|
||||
}
|
||||
if (!row.turnId) return
|
||||
state.chatSelectedTurnId = row.turnId
|
||||
const target = streamEl && streamEl.querySelector(`[data-turn-id="${row.turnId}"]`)
|
||||
if (target && typeof target.scrollIntoView === 'function') {
|
||||
target.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||
}
|
||||
// Re-render so the .active highlight lands on the row we just
|
||||
// clicked. Keeps drawer state in sync with the stream focus.
|
||||
refreshChatSideDrawerIfOpen()
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -4343,15 +4359,34 @@ function refreshSessionGraph() {
|
||||
const events = (meta && Array.isArray(meta.cachedEvents)) ? meta.cachedEvents : []
|
||||
api.renderSessionGraph(chatSessionGraphEl, {
|
||||
sessionId: state.activeSessionId || '',
|
||||
selectedTurnId: state.chatSelectedTurnId || null,
|
||||
events,
|
||||
onSelect(node) {
|
||||
if (!node || !node.turnId) return
|
||||
if (!node) return
|
||||
// Fork nodes point at a child session — clicking follows the
|
||||
// fork. Without this the cursor:pointer node reads as dead.
|
||||
if (node.kind === 'fork' && node.childSessionId) {
|
||||
try { selectSession(node.childSessionId) } catch (_) { /* stale id */ }
|
||||
return
|
||||
}
|
||||
// User nodes have no turnId — scroll to the message via data-seq.
|
||||
if (node.kind === 'user' && node.seq) {
|
||||
setChatView('list')
|
||||
const sTarget = streamEl && streamEl.querySelector(`[data-seq="${node.seq}"]`)
|
||||
if (sTarget && typeof sTarget.scrollIntoView === 'function') {
|
||||
sTarget.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||
}
|
||||
return
|
||||
}
|
||||
if (!node.turnId) return
|
||||
state.chatSelectedTurnId = node.turnId
|
||||
// Jump to the turn in the List view and focus it.
|
||||
setChatView('list')
|
||||
const target = streamEl && streamEl.querySelector(`[data-turn-id="${node.turnId}"]`)
|
||||
if (target && typeof target.scrollIntoView === 'function') {
|
||||
target.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||
}
|
||||
refreshChatSideDrawerIfOpen()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -178,13 +178,16 @@ function createStore() {
|
||||
// so without dedupe here, two-page seeding would double-insert every
|
||||
// event. Two-tier guard:
|
||||
// 1. WeakSet on the fixture object ref (fast path for the common
|
||||
// case where all pages read window.__dshRubricFusionSeed).
|
||||
// case where all pages read window.__dshRubricFusionSeed). Return
|
||||
// shape includes `deduped: true` on this fast-path hit so callers
|
||||
// can distinguish "already seeded" from "empty fixture".
|
||||
// 2. Per-event key dedupe inside addEvent() catches the case where a
|
||||
// caller passes a fresh literal that happens to carry the same
|
||||
// rows.
|
||||
// caller passes a fresh literal (or a deep-cloned copy) that
|
||||
// carries the same rows — the WeakSet won't help there because
|
||||
// the object identity differs.
|
||||
function loadFixture(json) {
|
||||
if (!json || typeof json !== 'object') return { rubrics: 0, events: 0 }
|
||||
if (state.loadedFixtures.has(json)) return { rubrics: 0, events: 0 }
|
||||
if (state.loadedFixtures.has(json)) return { rubrics: 0, events: 0, deduped: true }
|
||||
state.loadedFixtures.add(json)
|
||||
let rn = 0, en = 0
|
||||
for (const r of json.rubrics || []) { if (registerRubric(r)) rn++ }
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Auto-inlined fusion fixture. Renderer runs at file:// so we inline
|
||||
// docs/rubric-fusion-fixture.json here instead of relying on fetch().
|
||||
// To refresh: node scripts/regen-rubric-fusion-seed.js
|
||||
|
||||
'use strict'
|
||||
;(function () {
|
||||
if (typeof window === 'undefined') return
|
||||
@@ -5,7 +9,7 @@
|
||||
"_note": "Rubric fusion fixture — feeds Rubrics/Growth/Runtime demo. All demo-tier data.",
|
||||
"rubrics": [
|
||||
{
|
||||
"id": "svg-gen",
|
||||
"id": "svg-generation",
|
||||
"name": "SVG generation",
|
||||
"group": "interaction-reasoning",
|
||||
"description": "Iterative SVG editing over 3-5 turns. Judged for shape correctness, style consistency, and no-regression.",
|
||||
@@ -97,6 +101,74 @@
|
||||
"max": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "code-review",
|
||||
"name": "Code review",
|
||||
"group": "se-process",
|
||||
"description": "Reviewer rubric — style, correctness, test-coverage feedback quality.",
|
||||
"dims": [
|
||||
{
|
||||
"id": "verdict",
|
||||
"label": "Review verdict",
|
||||
"type": "categorical",
|
||||
"values": [
|
||||
"reject",
|
||||
"revise",
|
||||
"approve"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "correctness-score",
|
||||
"name": "Correctness score",
|
||||
"group": "fix-optimize",
|
||||
"description": "Continuous 0-1 correctness on trajectory's stated goal.",
|
||||
"dims": [
|
||||
{
|
||||
"id": "correctness",
|
||||
"label": "Correctness",
|
||||
"type": "continuous",
|
||||
"min": 0,
|
||||
"max": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "intent-triage",
|
||||
"name": "Intent triage",
|
||||
"group": "interaction-reasoning",
|
||||
"description": "Categorical verdict — bad · ok · good.",
|
||||
"dims": [
|
||||
{
|
||||
"id": "verdict",
|
||||
"label": "Triage verdict",
|
||||
"type": "categorical",
|
||||
"values": [
|
||||
"bad",
|
||||
"ok",
|
||||
"good"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "passes-bench",
|
||||
"name": "Passes bench",
|
||||
"group": "se-process",
|
||||
"description": "Boolean pass/fail on bench probe.",
|
||||
"dims": [
|
||||
{
|
||||
"id": "passes",
|
||||
"label": "Passes bench probe",
|
||||
"type": "boolean",
|
||||
"labels": {
|
||||
"true": "pass",
|
||||
"false": "fail"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"similarClasses": [
|
||||
@@ -117,7 +189,7 @@
|
||||
"events": [
|
||||
{
|
||||
"ts": 1720915200000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "shape-correct",
|
||||
"sessionId": "s-svg-1",
|
||||
"turnId": "t1",
|
||||
@@ -129,7 +201,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1720915200000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "style-consistent",
|
||||
"sessionId": "s-svg-1",
|
||||
"turnId": "t1",
|
||||
@@ -141,7 +213,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1720915200000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "no-regress",
|
||||
"sessionId": "s-svg-1",
|
||||
"turnId": "t1",
|
||||
@@ -153,7 +225,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721001600000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "shape-correct",
|
||||
"sessionId": "s-svg-2",
|
||||
"turnId": "t2",
|
||||
@@ -165,7 +237,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721001600000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "style-consistent",
|
||||
"sessionId": "s-svg-2",
|
||||
"turnId": "t2",
|
||||
@@ -177,7 +249,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721001600000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "no-regress",
|
||||
"sessionId": "s-svg-2",
|
||||
"turnId": "t2",
|
||||
@@ -189,7 +261,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721088000000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "shape-correct",
|
||||
"sessionId": "s-svg-3",
|
||||
"turnId": "t3",
|
||||
@@ -201,7 +273,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721088000000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "style-consistent",
|
||||
"sessionId": "s-svg-3",
|
||||
"turnId": "t3",
|
||||
@@ -213,7 +285,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721088000000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "no-regress",
|
||||
"sessionId": "s-svg-3",
|
||||
"turnId": "t3",
|
||||
@@ -225,7 +297,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721174400000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "shape-correct",
|
||||
"sessionId": "s-svg-4",
|
||||
"turnId": "t4",
|
||||
@@ -237,7 +309,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721174400000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "style-consistent",
|
||||
"sessionId": "s-svg-4",
|
||||
"turnId": "t4",
|
||||
@@ -249,7 +321,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721174400000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "no-regress",
|
||||
"sessionId": "s-svg-4",
|
||||
"turnId": "t4",
|
||||
@@ -261,7 +333,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721260800000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "shape-correct",
|
||||
"sessionId": "s-svg-5",
|
||||
"turnId": "t5",
|
||||
@@ -273,7 +345,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721260800000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "style-consistent",
|
||||
"sessionId": "s-svg-5",
|
||||
"turnId": "t5",
|
||||
@@ -285,7 +357,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721260800000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "no-regress",
|
||||
"sessionId": "s-svg-5",
|
||||
"turnId": "t5",
|
||||
@@ -297,7 +369,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721260800000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "shape-correct",
|
||||
"sessionId": "s-svg-live",
|
||||
"turnId": "tL",
|
||||
@@ -309,7 +381,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721260800000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "style-consistent",
|
||||
"sessionId": "s-svg-live",
|
||||
"turnId": "tL",
|
||||
@@ -321,7 +393,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721260800000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "no-regress",
|
||||
"sessionId": "s-svg-live",
|
||||
"turnId": "tL",
|
||||
@@ -333,7 +405,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721260800000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "shape-correct",
|
||||
"sessionId": "s-svg-live",
|
||||
"turnId": "tL",
|
||||
@@ -345,7 +417,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721260800000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "style-consistent",
|
||||
"sessionId": "s-svg-live",
|
||||
"turnId": "tL",
|
||||
@@ -357,7 +429,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721260800000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "no-regress",
|
||||
"sessionId": "s-svg-live",
|
||||
"turnId": "tL",
|
||||
@@ -369,7 +441,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721260800000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "shape-correct",
|
||||
"sessionId": "s-svg-live",
|
||||
"turnId": "tL",
|
||||
@@ -381,7 +453,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721260800000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "style-consistent",
|
||||
"sessionId": "s-svg-live",
|
||||
"turnId": "tL",
|
||||
@@ -393,7 +465,7 @@
|
||||
},
|
||||
{
|
||||
"ts": 1721260800000,
|
||||
"rubricId": "svg-gen",
|
||||
"rubricId": "svg-generation",
|
||||
"dimId": "no-regress",
|
||||
"sessionId": "s-svg-live",
|
||||
"turnId": "tL",
|
||||
@@ -618,6 +690,150 @@
|
||||
"harnessVersion": "v0.11",
|
||||
"model": "deepseek-r1",
|
||||
"dataMix": "mix-c"
|
||||
},
|
||||
{
|
||||
"ts": 1721088000000,
|
||||
"rubricId": "code-review",
|
||||
"dimId": "verdict",
|
||||
"sessionId": "s-cr-1",
|
||||
"turnId": "t1",
|
||||
"rolloutIdx": 1,
|
||||
"score": "revise",
|
||||
"harnessVersion": "v0.10",
|
||||
"model": "deepseek-r1",
|
||||
"dataMix": "mix-b"
|
||||
},
|
||||
{
|
||||
"ts": 1721174400000,
|
||||
"rubricId": "code-review",
|
||||
"dimId": "verdict",
|
||||
"sessionId": "s-cr-2",
|
||||
"turnId": "t2",
|
||||
"rolloutIdx": 1,
|
||||
"score": "approve",
|
||||
"harnessVersion": "v0.11",
|
||||
"model": "deepseek-r1",
|
||||
"dataMix": "mix-b"
|
||||
},
|
||||
{
|
||||
"ts": 1721260800000,
|
||||
"rubricId": "code-review",
|
||||
"dimId": "verdict",
|
||||
"sessionId": "s-cr-3",
|
||||
"turnId": "t3",
|
||||
"rolloutIdx": 1,
|
||||
"score": "approve",
|
||||
"harnessVersion": "v0.11",
|
||||
"model": "deepseek-r1",
|
||||
"dataMix": "mix-c"
|
||||
},
|
||||
{
|
||||
"ts": 1721088000000,
|
||||
"rubricId": "correctness-score",
|
||||
"dimId": "correctness",
|
||||
"sessionId": "s-cs-1",
|
||||
"turnId": "t1",
|
||||
"rolloutIdx": 1,
|
||||
"score": 0.62,
|
||||
"harnessVersion": "v0.10",
|
||||
"model": "deepseek-r1",
|
||||
"dataMix": "mix-b"
|
||||
},
|
||||
{
|
||||
"ts": 1721174400000,
|
||||
"rubricId": "correctness-score",
|
||||
"dimId": "correctness",
|
||||
"sessionId": "s-cs-2",
|
||||
"turnId": "t2",
|
||||
"rolloutIdx": 1,
|
||||
"score": 0.78,
|
||||
"harnessVersion": "v0.11",
|
||||
"model": "deepseek-r1",
|
||||
"dataMix": "mix-b"
|
||||
},
|
||||
{
|
||||
"ts": 1721260800000,
|
||||
"rubricId": "correctness-score",
|
||||
"dimId": "correctness",
|
||||
"sessionId": "s-cs-3",
|
||||
"turnId": "t3",
|
||||
"rolloutIdx": 1,
|
||||
"score": 0.85,
|
||||
"harnessVersion": "v0.11",
|
||||
"model": "deepseek-r1",
|
||||
"dataMix": "mix-c"
|
||||
},
|
||||
{
|
||||
"ts": 1721088000000,
|
||||
"rubricId": "intent-triage",
|
||||
"dimId": "verdict",
|
||||
"sessionId": "s-it-1",
|
||||
"turnId": "t1",
|
||||
"rolloutIdx": 1,
|
||||
"score": "ok",
|
||||
"harnessVersion": "v0.10",
|
||||
"model": "deepseek-r1",
|
||||
"dataMix": "mix-b"
|
||||
},
|
||||
{
|
||||
"ts": 1721174400000,
|
||||
"rubricId": "intent-triage",
|
||||
"dimId": "verdict",
|
||||
"sessionId": "s-it-2",
|
||||
"turnId": "t2",
|
||||
"rolloutIdx": 1,
|
||||
"score": "good",
|
||||
"harnessVersion": "v0.11",
|
||||
"model": "deepseek-r1",
|
||||
"dataMix": "mix-b"
|
||||
},
|
||||
{
|
||||
"ts": 1721260800000,
|
||||
"rubricId": "intent-triage",
|
||||
"dimId": "verdict",
|
||||
"sessionId": "s-it-3",
|
||||
"turnId": "t3",
|
||||
"rolloutIdx": 1,
|
||||
"score": "good",
|
||||
"harnessVersion": "v0.11",
|
||||
"model": "deepseek-r1",
|
||||
"dataMix": "mix-c"
|
||||
},
|
||||
{
|
||||
"ts": 1721088000000,
|
||||
"rubricId": "passes-bench",
|
||||
"dimId": "passes",
|
||||
"sessionId": "s-pb-1",
|
||||
"turnId": "t1",
|
||||
"rolloutIdx": 1,
|
||||
"score": false,
|
||||
"harnessVersion": "v0.10",
|
||||
"model": "deepseek-r1",
|
||||
"dataMix": "mix-b"
|
||||
},
|
||||
{
|
||||
"ts": 1721174400000,
|
||||
"rubricId": "passes-bench",
|
||||
"dimId": "passes",
|
||||
"sessionId": "s-pb-2",
|
||||
"turnId": "t2",
|
||||
"rolloutIdx": 1,
|
||||
"score": true,
|
||||
"harnessVersion": "v0.11",
|
||||
"model": "deepseek-r1",
|
||||
"dataMix": "mix-b"
|
||||
},
|
||||
{
|
||||
"ts": 1721260800000,
|
||||
"rubricId": "passes-bench",
|
||||
"dimId": "passes",
|
||||
"sessionId": "s-pb-3",
|
||||
"turnId": "t3",
|
||||
"rolloutIdx": 1,
|
||||
"score": true,
|
||||
"harnessVersion": "v0.11",
|
||||
"model": "deepseek-r1",
|
||||
"dataMix": "mix-c"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -12574,6 +12574,18 @@ button.artifact-version:hover {
|
||||
border: 1px dashed var(--border); border-radius: 4px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.artifact-evolution-banner {
|
||||
padding: 6px 8px; font-size: 11px; line-height: 1.4;
|
||||
border-left: 2px solid var(--border);
|
||||
background: var(--bg-elev);
|
||||
border-radius: 0 4px 4px 0;
|
||||
margin: 4px 0 6px 0;
|
||||
}
|
||||
.artifact-panel-empty {
|
||||
padding: 12px 10px;
|
||||
text-align: center;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.artifact-evolution-diff-body {
|
||||
margin-top: 4px;
|
||||
border: 1px solid var(--border); border-radius: 4px;
|
||||
|
||||
@@ -472,4 +472,17 @@
|
||||
// tri-view instead of on the index. Auto-drill only fires when the caller
|
||||
// has a valid sessionId; the internal path already handles refresh + rehydrate.
|
||||
window.__dshTracingPage = { show, closeDrill, openDrill }
|
||||
|
||||
// Runtime → Rubric grid cell click dispatches `dsh:rubric-cell-jump`. The
|
||||
// upstream fires with { sessionId, turnId } detail and calls __dshTabs to
|
||||
// switch, but drops the ids. Wire the drill here so clicking a cell
|
||||
// actually lands the user inside that session's trace — the whole value
|
||||
// prop of the grid.
|
||||
if (typeof window !== 'undefined' && typeof window.addEventListener === 'function') {
|
||||
window.addEventListener('dsh:rubric-cell-jump', function (ev) {
|
||||
const detail = ev && ev.detail
|
||||
if (!detail || !detail.sessionId) return
|
||||
try { openDrill(detail.sessionId) } catch (_) { /* drill unavailable — swallow */ }
|
||||
})
|
||||
}
|
||||
})()
|
||||
|
||||
Reference in New Issue
Block a user