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
8 changes: 5 additions & 3 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,18 @@ and public [`ace-core==0.8.0`](https://pypi.org/project/ace-core/0.8.0/) package
developer-preview release line. Atrium is a repository-delivered optional workspace rather than a
second state or authority path.

### Active hardening — Atrium live onboarding binding
### Passed hardening — Atrium live onboarding binding

The post-0.8 candidate closes one experience gap without creating a new product layer. Core now
projects one inert onboarding profile plus the exact append-only Intelligence Builder session
history through the same authenticated resource plane Atrium already consumes. Atrium renders
proposal, working, blocked, retrying, and complete states from those durable records and opens the
first Brief only after `first_briefing_ready`. World AI supplies the paired domain profile and a
four-agent `Connect -> Map -> Watch -> Brief` acceptance journey over admitted official-source
evidence. The work remains candidate until both repositories pass review and installed-artifact
reproduction; it does not change the 0.9 Collaborative Intelligence promise.
evidence. Core PR #145 and World PR #24 landed after repository, installed-artifact, browser,
naked-kernel, and package checks passed. The follow-on demo-polish packet adds a local-only replay
of an immutable domain resource page and replaces internal persistence language with user-facing
setup status. It does not change the 0.9 Collaborative Intelligence promise.

The prior [0.7.0 GitHub Release](https://github.com/augmented-cognition-engine/core/releases/tag/v0.7.0)
and [`ace-core==0.7.0`](https://pypi.org/project/ace-core/0.7.0/) package remain the passed
Expand Down
12 changes: 5 additions & 7 deletions core/ui/canvas/src/app/atrium/IntelligenceOS.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ import {
} from './onboardingModel'
import { pageFreshness, productDisplayName } from './experienceModel'
import {
EXPLICITLY_DEGRADED_RESOURCE_KINDS,
groupResources,
kindLabel,
type ResourceGroups,
} from './intelligenceModel'
import { ResourceCard } from './ResourceCard'
Expand Down Expand Up @@ -384,9 +382,9 @@ export function IntelligenceOS() {
{page?.state === 'degraded' && (
<Alert className="mb-6 border-warning/45 bg-warning/5">
<CircleAlert className="size-4" />
<AlertTitle>ACE is showing the truth it can currently prove</AlertTitle>
<AlertTitle>Some evidence still needs review</AlertTitle>
<AlertDescription>
{EXPLICITLY_DEGRADED_RESOURCE_KINDS.map(kindLabel).join(', ')} remain explicit gaps; available intelligence and provenance are still shown.
Some source-health, confidence, conflict, and revision checks are incomplete. Available intelligence and citations remain visible.
</AlertDescription>
</Alert>
)}
Expand All @@ -403,11 +401,11 @@ export function IntelligenceOS() {

<footer className="mx-auto flex w-full max-w-[1500px] flex-wrap items-center gap-2 px-5 pb-6 text-[10px] text-muted-foreground md:px-8">
<Route className="size-3" />
<span>One governed resource plane</span>
<span>One current intelligence picture</span>
<span>·</span>
<span>{page?.items.length ?? 0} current resources</span>
<span>{page?.items.length ?? 0} cited records</span>
<span>·</span>
<span>exact provenance retained</span>
<span>Sources and history preserved</span>
</footer>
<OnboardingPreview
open={onboardingOpen}
Expand Down
28 changes: 20 additions & 8 deletions core/ui/canvas/src/app/atrium/OnboardingPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ function buildLanes(session: IntelligenceBuilderSession | null, watchCount: numb
const watchState = laneState(rank, 5, 6)
const briefingState = laneState(rank, 6, 7)
const lanes: BuildLane[] = [
{ label: 'Connect and validate evidence', result: evidenceState === 'complete' ? 'Approved source profile retained' : 'Connection Agent is validating permitted sources', state: evidenceState },
{ label: 'Map entities and concepts', result: conceptState === 'complete' ? 'Approved concept model retained' : 'Ontology Agent is grounding the concept map', state: conceptState },
{ label: 'Configure governed watches', result: watchState === 'complete' ? 'Approved watch model retained' : `Intelligence Agent is evaluating ${watchCount} starting areas`, state: watchState },
{ label: 'Assemble the first cited Brief', result: briefingState === 'complete' ? 'First cited Brief preview retained' : 'Briefing Agent is preserving claims and citations', state: briefingState },
{ label: 'Connect and validate evidence', result: evidenceState === 'complete' ? 'Approved evidence connected' : 'Connection Agent is validating permitted sources', state: evidenceState },
{ label: 'Map entities and concepts', result: conceptState === 'complete' ? 'Entities and concepts mapped' : 'Ontology Agent is grounding the concept map', state: conceptState },
{ label: 'Configure governed watches', result: watchState === 'complete' ? 'Watch plan approved' : `Intelligence Agent is evaluating ${watchCount} starting areas`, state: watchState },
{ label: 'Assemble the first cited Brief', result: briefingState === 'complete' ? 'First cited Brief ready' : 'Briefing Agent is preserving claims and citations', state: briefingState },
]

if (session.stage === 'blocked') {
Expand Down Expand Up @@ -155,8 +155,8 @@ export function OnboardingPreview({
<div className="flex items-center gap-2 font-mono text-[9px] font-semibold uppercase tracking-[0.17em] text-brand">
<Sparkles className="size-3.5" /> Build your intelligence
</div>
<Badge variant="outline" className="rounded-sm font-mono text-[9px]">
{session === null ? 'Proposal only' : `Live session · r${session.sequence}`}
<Badge variant="outline" className="mr-6 rounded-sm font-mono text-[9px]">
{session === null ? 'Proposal only' : `Live · step ${session.sequence}`}
</Badge>
</div>
<div className="mt-3 flex gap-1.5" aria-label={`Step ${step + 1} of 4`}>
Expand Down Expand Up @@ -218,13 +218,25 @@ export function OnboardingPreview({
<>
<DialogHeader className="max-w-2xl">
<DialogTitle className="text-2xl tracking-tight">{firstBriefReady ? 'Your first picture is ready' : session?.stage === 'blocked' ? 'ACE needs your attention' : session === null ? 'Your governed plan is ready' : 'Your first picture is assembling'}</DialogTitle>
<DialogDescription>{session === null ? 'These are proposed steps. No agent action is represented as complete until Core admits its durable session record.' : 'This status comes from the governed, append-only Intelligence Builder session—not UI animation.'}</DialogDescription>
<DialogDescription>
{session === null
? 'Review the plan before ACE connects sources or starts watching.'
: firstBriefReady
? 'ACE built this picture from the sources and watch settings you approved.'
: session.stage === 'blocked'
? 'ACE paused safely before changing your intelligence picture.'
: 'ACE is assembling the picture from the sources and watch settings you approved.'}
</DialogDescription>
</DialogHeader>
<div className="mt-7 space-y-2">
{lanes.map((lane) => <BuildStep key={lane.label} {...lane} />)}
</div>
<div className="mt-5 rounded-lg border bg-card p-4 text-xs text-muted-foreground">
{session === null ? 'Reviewing this plan grants no source, monitor, or activation authority.' : `${session.artifacts.length} exact builder artifact${session.artifacts.length === 1 ? '' : 's'} retained · session revision ${session.sequence}`}
{session === null
? 'Reviewing this plan changes nothing until you approve it.'
: firstBriefReady
? 'First cited Brief ready · Setup saved'
: `Setup saved · Step ${session.sequence}`}
</div>
</>
)}
Expand Down
23 changes: 16 additions & 7 deletions core/ui/canvas/tests/e2e/atrium-domain-resource-page.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,6 @@ test('Atrium renders an external domain resource page without domain UI code', a
items: Array<{ reference: { resource_kind: string }; title: string }>
}

await page.route('**/auth/token', (route) =>
route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ token: 'fixture-token' }) }),
)
await page.route('**/v1/intelligence/resources/query', (route) =>
route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(resourcePage) }),
)

await page.goto('/atrium')

const brief = resourcePage.items.find((item) => item.reference.resource_kind === 'brief')
Expand All @@ -26,8 +19,24 @@ test('Atrium renders an external domain resource page without domain UI code', a
if (brief !== undefined) {
await expect(page.getByRole('button', { name: `Open ${brief.title}` })).toBeVisible()
}
if ((resourcePage as { state?: string }).state === 'degraded') {
await expect(page.getByText('Some evidence still needs review')).toBeVisible()
}
await expect(page.getByText(`${resourcePage.items.length} cited records`)).toBeVisible()
await page.screenshot({ path: testInfo.outputPath('atrium-domain-resource-page.png'), fullPage: true })

const viewBuild = page.getByRole('button', { name: 'View build' })
if (await viewBuild.isVisible()) {
await viewBuild.click()
await page.getByRole('button', { name: 'Continue' }).click()
await page.getByRole('button', { name: 'Continue' }).click()
await page.getByRole('button', { name: 'View live build' }).click()
await expect(page.getByRole('heading', { name: 'Your first picture is ready' })).toBeVisible()
await expect(page.getByText('ACE built this picture from the sources and watch settings you approved.')).toBeVisible()
await expect(page.getByText('First cited Brief ready · Setup saved')).toBeVisible()
await page.getByRole('button', { name: 'Open my first briefing' }).click()
}

await page.setViewportSize({ width: 390, height: 844 })
await page.goto('/atrium')
await expect(page.getByRole('heading', { name: 'Intelligence' })).toBeVisible()
Expand Down
59 changes: 58 additions & 1 deletion core/ui/canvas/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,63 @@ function serveExtensionStaticHtml(): Plugin {
}
}

/**
* Replay one immutable Intelligence resource page through Atrium during local demos.
*
* This is deliberately a Vite-only presentation seam. It does not create a store,
* admit evidence, grant authority, or exist in the production canvas host. A domain
* repository generates the page through Core's real resource projection, then points
* this local server at the resulting JSON artifact with ACE_ATRIUM_RESOURCE_PAGE.
*/
function serveAtriumResourceReplay(): Plugin {
const configuredPath = process.env.ACE_ATRIUM_RESOURCE_PAGE
if (configuredPath === undefined || configuredPath.trim() === '') {
return { name: 'ace:atrium-resource-replay-disabled' }
}

const resourcePath = path.resolve(configuredPath)
let resourceBody: string
try {
const raw = readFileSync(resourcePath, 'utf-8')
if (raw.length > 10_000_000) throw new Error('resource page exceeds the 10 MB local replay limit')
const parsed = JSON.parse(raw) as { contract?: unknown; items?: unknown }
if (
parsed.contract !== 'ace.intelligence.resource-plane-page/v1alpha1' ||
!Array.isArray(parsed.items)
) {
throw new Error('resource page does not match the public Intelligence page contract')
}
resourceBody = JSON.stringify(parsed)
} catch (error) {
throw new Error(`ACE_ATRIUM_RESOURCE_PAGE could not be loaded from ${resourcePath}: ${String(error)}`)
}

return {
name: 'ace:atrium-resource-replay',
configureServer(server) {
server.config.logger.info(`Atrium is replaying ${resourcePath}`)
server.middlewares.use((req, res, next) => {
const [pathname] = (req.url ?? '').split('?')
if (req.method === 'POST' && pathname === '/auth/token') {
res.setHeader('Content-Type', 'application/json; charset=utf-8')
res.setHeader('Cache-Control', 'no-store')
res.statusCode = 200
res.end(JSON.stringify({ token: 'atrium-local-replay' }))
return
}
if (req.method === 'POST' && pathname === '/v1/intelligence/resources/query') {
res.setHeader('Content-Type', 'application/json; charset=utf-8')
res.setHeader('Cache-Control', 'no-store')
res.statusCode = 200
res.end(resourceBody)
return
}
next()
})
},
}
}

/** The kernel's own routes. An extension may not claim any of these (fail-closed). */
const kernelProxy: Record<string, ProxyOptions | string> = Object.fromEntries(
KERNEL_DEV_PROXY_ROUTES.map(([route, ws]) => [
Expand All @@ -116,7 +173,7 @@ const kernelProxy: Record<string, ProxyOptions | string> = Object.fromEntries(
)

export default defineConfig({
plugins: [react(), tailwindcss(), serveExtensionStaticHtml()],
plugins: [serveAtriumResourceReplay(), react(), tailwindcss(), serveExtensionStaticHtml()],
// Yjs constructor checks break if two copies of the module end up in the
// bundle (one from `import * as Y from 'yjs'`, one from `y-websocket`
// pulling its own pre-bundled copy). Force a single resolved instance.
Expand Down
7 changes: 5 additions & 2 deletions docs/evidence/atrium-live-onboarding-candidate-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,8 @@ corroboration claim.
- the Atrium production build completes;
- focused World tests reproduce the four-agent journey and exact 23-resource Atrium page.

Full repository, installed-wheel, accessibility, and paired-PR review gates remain required before
this candidate advances to passed.
The paired implementation landed through Core PR #145 and World PR #24 after the supported Core,
World, Canvas, naked-kernel, Docker, installed-wheel, and browser gates passed. Post-landing demo
hardening adds a Vite-only immutable-page replay seam and removes internal persistence vocabulary
from the leadership-facing journey. Live network freshness, general accessibility review, and
broader AI-area coverage remain separate follow-on gates.