Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion core/engine/atrium/static/assets/index-CZdqexKr.css

This file was deleted.

1 change: 1 addition & 0 deletions core/engine/atrium/static/assets/index-Rt6Lb9Op.css

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions core/engine/atrium/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
<link rel="icon" type="image/png" sizes="128x128" href="/brand/ace_logo_fixed_128.png" />
<link rel="icon" type="image/png" sizes="256x256" href="/brand/ace_logo_fixed_256.png" />
<link rel="apple-touch-icon" sizes="512x512" href="/brand/ace_logo_fixed_512.png" />
<script type="module" crossorigin src="/assets/index-BaLqTrAb.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CZdqexKr.css">
<script type="module" crossorigin src="/assets/index-BedGT8MQ.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Rt6Lb9Op.css">
</head>
<body>
<div id="root"></div>
Expand Down
56 changes: 56 additions & 0 deletions core/ui/canvas/src/app/atrium/AskAce.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { fireEvent, render, screen } from '@testing-library/react'
import { describe, expect, it } from 'vitest'

import type { IntelligenceResourceRecord } from '@/api/intelligenceResourcesApi'

import { AskAce } from './AskAce'

const brief = {
contract: 'ace.intelligence.resource-plane-record/v1alpha1',
reference: {
contract: 'ace.intelligence.resource-plane-reference/v1alpha1',
product_id: 'product:world-ai',
resource_kind: 'brief',
resource_id: 'brief:ai-policy',
resource_digest: `sha256:${'a'.repeat(64)}`,
resource_contract: 'ace.intelligence.brief/v1alpha1',
revision: 1,
as_of: '2026-08-13T12:00:00Z',
available_at: '2026-08-13T12:00:00Z',
},
availability: 'available',
title: 'AI policy moved into implementation',
summary: 'A directive now has reported implementation activity.',
subject_refs: [],
provenance: [{
contract: 'ace.intelligence.resource-plane-reference/v1alpha1',
product_id: 'product:world-ai',
resource_kind: 'source',
resource_id: 'source:federal-register',
resource_digest: `sha256:${'b'.repeat(64)}`,
resource_contract: 'ace.intelligence.source/v1alpha1',
revision: 1,
as_of: '2026-08-13T11:00:00Z',
available_at: '2026-08-13T11:00:00Z',
}],
supersedes: null,
payload: {
what_changed: 'The policy moved from directive to reported implementation.',
why_it_matters: 'Execution can now be evaluated against the original intent.',
when_it_changed: 'The implementation report followed the directive.',
},
degraded_reason_refs: [],
} satisfies IntelligenceResourceRecord

describe('Ask ACE', () => {
it('separates the grounded answer from the exact evidence used', () => {
render(<AskAce items={[brief]} />)

fireEvent.click(screen.getByRole('button', { name: 'What changed most recently?' }))

expect(screen.getByRole('region', { name: 'Ask ACE answer' })).toBeTruthy()
expect(screen.getByText('The policy moved from directive to reported implementation.')).toBeTruthy()
expect(screen.getByRole('complementary', { name: 'Evidence used for this answer' })).toBeTruthy()
expect(screen.getByText('AI policy moved into implementation')).toBeTruthy()
})
})
93 changes: 48 additions & 45 deletions core/ui/canvas/src/app/atrium/AskAce.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,58 +103,61 @@ export function AskAce({ items }: { readonly items: readonly IntelligenceResourc
I do not have enough governed evidence to answer that yet. Add a source or broaden a monitor; ACE will not fill the gap with an unsupported claim.
</p>
) : (
<div className="mt-3 space-y-4">
<p className="border-l-2 border-brand pl-3 text-[15px] font-medium leading-6 text-foreground">
{answer.conclusion}
</p>

{(answer.whyItMatters !== null || answer.whenItChanged !== null) && (
<div className="grid gap-px overflow-hidden rounded-lg border bg-border sm:grid-cols-2">
{answer.whyItMatters !== null && (
<div className="bg-card p-3">
<div className="font-mono text-[8px] font-semibold uppercase tracking-[0.15em] text-brand">Why it matters</div>
<p className="mt-1.5 text-xs leading-5 text-foreground/85">{answer.whyItMatters}</p>
</div>
)}
{answer.whenItChanged !== null && (
<div className="bg-card p-3">
<div className="flex items-center gap-1.5 font-mono text-[8px] font-semibold uppercase tracking-[0.15em] text-brand">
<Clock3 className="size-3" /> When
</div>
<p className="mt-1.5 text-xs leading-5 text-foreground/85">{answer.whenItChanged}</p>
</div>
)}
<div className="mt-3 grid gap-4 lg:grid-cols-[minmax(0,1.15fr)_minmax(18rem,0.85fr)]">
<div className="space-y-4">
<div>
<div className="font-mono text-[8px] font-semibold uppercase tracking-[0.15em] text-brand">Answer</div>
<p className="mt-2 border-l-2 border-brand pl-3 text-[15px] font-medium leading-6 text-foreground">
{answer.conclusion}
</p>
</div>
)}

<div>
<div className="mb-2 flex items-center gap-1.5 font-mono text-[9px] font-semibold uppercase tracking-[0.15em] text-muted-foreground">
<GitBranch className="size-3" /> Evidence trail
</div>
<div className="space-y-2">
{answer.evidence.slice(0, 3).map((match, index) => (
<div key={`${match.reference.resource_id}:${match.reference.revision}`} className="flex gap-3">
<span className="font-mono text-[10px] text-brand">[{index + 1}]</span>
<div className="min-w-0">
<div className="text-sm font-medium leading-snug">{match.title}</div>
{match.summary !== null && (
<p className="mt-1 text-xs leading-relaxed text-muted-foreground">
{match.summary}
</p>
{(answer.whyItMatters !== null || answer.whenItChanged !== null) && (
<div className="grid gap-px overflow-hidden rounded-lg border bg-border sm:grid-cols-2">
{answer.whyItMatters !== null && (
<div className="bg-card p-3">
<div className="font-mono text-[8px] font-semibold uppercase tracking-[0.15em] text-brand">Why it matters</div>
<p className="mt-1.5 text-xs leading-5 text-foreground/85">{answer.whyItMatters}</p>
</div>
)}
{answer.whenItChanged !== null && (
<div className="bg-card p-3">
<div className="flex items-center gap-1.5 font-mono text-[8px] font-semibold uppercase tracking-[0.15em] text-brand">
<Clock3 className="size-3" /> When
</div>
<p className="mt-1.5 text-xs leading-5 text-foreground/85">{answer.whenItChanged}</p>
</div>
)}
<div className="mt-1.5 font-mono text-[10px] text-muted-foreground">
{kindLabel(match.reference.resource_kind)} · revision {match.reference.revision} · {match.provenance.length} evidence links
</div>
</div>
</div>
))}
)}

<div className="flex items-start gap-2 border-t border-border/70 pt-3 text-[11px] leading-5 text-muted-foreground">
<CircleAlert className="mt-0.5 size-3.5 shrink-0" />
<span>{answer.limitation}</span>
</div>
</div>

<div className="flex items-start gap-2 border-t border-border/70 pt-3 text-[11px] leading-5 text-muted-foreground">
<CircleAlert className="mt-0.5 size-3.5 shrink-0" />
<span>{answer.limitation}</span>
</div>
<aside className="rounded-lg border bg-card/70 p-3.5" aria-label="Evidence used for this answer">
<div className="mb-3 flex items-center justify-between gap-3">
<div className="flex items-center gap-1.5 font-mono text-[9px] font-semibold uppercase tracking-[0.15em] text-muted-foreground">
<GitBranch className="size-3" /> Evidence used
</div>
<Badge variant="outline" className="rounded-sm font-mono text-[8px]">{answer.evidence.length} records</Badge>
</div>
<div className="divide-y divide-border/70">
{answer.evidence.slice(0, 4).map((match, index) => (
<div key={`${match.reference.resource_id}:${match.reference.revision}`} className="flex gap-3 py-3 first:pt-0 last:pb-0">
<span className="font-mono text-[10px] text-brand">[{index + 1}]</span>
<div className="min-w-0">
<div className="text-xs font-medium leading-snug">{match.title}</div>
<div className="mt-1.5 font-mono text-[9px] text-muted-foreground">
{kindLabel(match.reference.resource_kind)} · r{match.reference.revision} · {match.provenance.length} links
</div>
</div>
</div>
))}
</div>
</aside>
</div>
)}
</div>
Expand Down
16 changes: 15 additions & 1 deletion core/ui/canvas/src/app/atrium/IntelligenceOS.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo, useState } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import {
Activity,
Expand Down Expand Up @@ -428,6 +428,7 @@ export function IntelligenceOS() {
const productName = productDisplayName(page?.product_id)
const freshness = pageFreshness(page)
const [onboardingOpen, setOnboardingOpen] = useState(false)
const onboardingPresented = useRef(false)
const onboardingProfiles = useMemo(
() => onboardingProfilesFromResources(
page?.items ?? [],
Expand All @@ -437,6 +438,19 @@ export function IntelligenceOS() {
)
const onboardingSession = useMemo(() => onboardingSessionFromResources(page?.items ?? []), [page?.items])

useEffect(() => {
if (
onboardingPresented.current
|| loading
|| error !== null
|| installedCatalog.length === 0
|| onboardingSession !== null
|| groups.intelligence.length > 0
) return
onboardingPresented.current = true
setOnboardingOpen(true)
}, [error, groups.intelligence.length, installedCatalog.length, loading, onboardingSession])

function openFirstBrief() {
requestAnimationFrame(() => document.getElementById('latest-brief')?.scrollIntoView({ behavior: 'smooth' }))
}
Expand Down
62 changes: 62 additions & 0 deletions core/ui/canvas/src/app/atrium/OnboardingPreview.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { fireEvent, render, screen } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'

import { OnboardingPreview } from './OnboardingPreview'
import type { IntelligenceOnboardingProfile } from './onboardingModel'

const profile: IntelligenceOnboardingProfile = {
contract: 'ace.intelligence.onboarding-profile/v1alpha1',
profile_id: 'onboarding_profile:world',
topic_id: 'artificial-intelligence',
display_name: 'AI Command Center',
domain_label: 'World Intelligence',
topic_label: 'Artificial intelligence',
prompt: 'What do you need to stay ahead of?',
description: 'Build a cited picture of meaningful AI change.',
starter_prompts: ['Keep me ahead of meaningful AI changes.'],
outcomes: [{
outcome_id: 'track-ai',
label: 'Track AI change',
description: 'Follow material capability, policy, and adoption shifts.',
icon_hint: 'research',
recommended_topic_labels: ['Capability', 'Policy'],
recommended_intelligence_labels: ['AI shifts'],
}],
source_groups: [{
source_group_id: 'official-records',
label: 'Official records',
description: 'Primary official evidence.',
evidence_role: 'authoritative_record',
source_ids: ['federal-register'],
source_labels: ['Federal Register'],
access_label: 'Recorded public evidence',
default_selected: true,
}],
cadences: [{ cadence_id: 'daily', label: 'Daily', description: 'Orient me daily.' }],
default_cadence_id: 'daily',
completion_label: 'Open the first Brief',
}

describe('Atrium Intelligence Builder onboarding', () => {
it('starts with the intelligence choice before asking for intent', () => {
render(
<OnboardingPreview
open
onOpenChange={vi.fn()}
profiles={[profile]}
session={null}
onStartBuild={vi.fn()}
onOpenBrief={vi.fn()}
/>,
)

expect(screen.getByRole('heading', { name: 'What kind of intelligence do you want to build?' })).toBeTruthy()
expect(screen.getByText('World Intelligence')).toBeTruthy()
expect(screen.queryByLabelText('Describe the intelligence you want')).toBeNull()

fireEvent.click(screen.getByRole('button', { name: /Use this intelligence/ }))

expect(screen.getByRole('heading', { name: 'What do you need to stay ahead of?' })).toBeTruthy()
expect(screen.getByLabelText('Describe the intelligence you want')).toBeTruthy()
})
})
Loading