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
23 changes: 10 additions & 13 deletions apps/web/src/components/OneLoopStudio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
studioEventsEmptyMessage,
studioExportFilename,
studioExportToastMessage,
studioFormationSupplementalEntities,
studioInvalidHandoffMessage,
studioPackCitation,
studioPackFormation,
Expand Down Expand Up @@ -281,6 +282,11 @@
packTools,
);
}, [selected, packFormation.tools]);
const stackChecks = packFormation.checks;
const supplementalEntities = studioFormationSupplementalEntities(
packFormation.tools,
linkedSop?.entities,
);

useEffect(() => {
setCompletedChecks([]);
Expand Down Expand Up @@ -461,7 +467,7 @@
const started = await startVideoToActions(payload);
if (!started.ok || !started.runId) {
if (started.status === 401 || started.status === 403) {
window.location.href = `/login?callbackUrl=${encodeURIComponent(CANONICAL_STUDIO_PATH)}`;

Check warning on line 470 in apps/web/src/components/OneLoopStudio.tsx

View workflow job for this annotation

GitHub Actions / lint-frontend

Do not use `window.location.href` to navigate to internal Next.js pages. Use `redirect()` in the render phase, or `useRouter().push()` in Client Components' event handlers instead. See: https://nextjs.org/docs/messages/no-location-assign-relative-destination

Check warning on line 470 in apps/web/src/components/OneLoopStudio.tsx

View workflow job for this annotation

GitHub Actions / build

Do not use `window.location.href` to navigate to internal Next.js pages. Use `redirect()` in the render phase, or `useRouter().push()` in Client Components' event handlers instead. See: https://nextjs.org/docs/messages/no-location-assign-relative-destination
return;
}
setMessage(started.error || started.message || 'Could not start Act.');
Expand Down Expand Up @@ -576,7 +582,7 @@
transcript: usableProvidedTranscript(selected?.transcript),
});
if (started.status === 401 || started.status === 403) {
window.location.href = `/login?callbackUrl=${encodeURIComponent(CANONICAL_STUDIO_PATH)}`;

Check warning on line 585 in apps/web/src/components/OneLoopStudio.tsx

View workflow job for this annotation

GitHub Actions / lint-frontend

Do not use `window.location.href` to navigate to internal Next.js pages. Use `redirect()` in the render phase, or `useRouter().push()` in Client Components' event handlers instead. See: https://nextjs.org/docs/messages/no-location-assign-relative-destination

Check warning on line 585 in apps/web/src/components/OneLoopStudio.tsx

View workflow job for this annotation

GitHub Actions / build

Do not use `window.location.href` to navigate to internal Next.js pages. Use `redirect()` in the render phase, or `useRouter().push()` in Client Components' event handlers instead. See: https://nextjs.org/docs/messages/no-location-assign-relative-destination
return;
}
if (!started.ok || !started.runId) {
Expand Down Expand Up @@ -931,7 +937,7 @@
</h2>
</div>
<div className="flex flex-wrap gap-2 px-4 py-3">
{linkedSop.entities.length === 0 && packFormation.tools.length === 0 && (
{supplementalEntities.length === 0 && packFormation.tools.length === 0 && (
<p className="text-sm text-white/40">No catalogued tools in this transcript.</p>
)}
{packFormation.tools.map((tool) => (
Expand All @@ -942,14 +948,7 @@
<span className="font-medium text-white">{tool.name}</span>
</span>
))}
{linkedSop.entities
.filter(
(entity) =>
!packFormation.tools.some(
(tool) => tool.name.toLowerCase() === entity.name.toLowerCase(),
),
)
.map((entity) => (
{supplementalEntities.map((entity) => (
<span
key={entity.name}
className="inline-flex items-center gap-2 rounded-full border border-white/15 bg-white/5 px-3 py-1.5 text-sm"
Expand Down Expand Up @@ -1017,17 +1016,15 @@
))}
</ol>

{linkedSop.checklist.some((item) => item.source === 'stack') && (
{stackChecks.length > 0 && (
<>
<div className="border-t border-white/10 px-4 py-3">
<h2 className="text-xs font-semibold uppercase tracking-[0.16em] text-white/45">
Stack checks
</h2>
</div>
<ul className="divide-y divide-white/5">
{linkedSop.checklist
.filter((item) => item.source === 'stack')
.map((item) => {
{stackChecks.map((item) => {
const checked = completedChecks.includes(item.id);
const status = stackCheckStatus(item, completedChecks, 'anonymous');
return (
Expand Down
31 changes: 31 additions & 0 deletions apps/web/src/lib/__tests__/studio-pipeline-status.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
studioVerifiedLiveUrl,
studioInvalidHandoffMessage,
studioPackCitation,
studioFormationSupplementalEntities,
studioPackFormation,
studioPasteOutcomeMessage,
studioPlayerOverlay,
Expand Down Expand Up @@ -155,9 +156,39 @@ describe('studio-pipeline-status', () => {
expect(studio).toContain('studioPackFormation');
expect(studio).toContain('data-testid="pack-architecture"');
expect(studio).toContain('data-testid="pack-artifacts"');
expect(studio).toContain('packFormation.checks');
expect(studio).not.toMatch(/router\.(push|replace)\(['"]\/dashboard/);
});

it('suppresses transcript-only tool chips when pack.stack.tools are grounded', () => {
expect(
studioFormationSupplementalEntities(
[{ name: 'Cloudflare' }, { name: 'x402' }],
[
{
name: 'Shopify',
kind: 'platform',
officialUrl: 'https://shopify.dev',
docsUrl: 'https://shopify.dev/docs',
timestamps: [],
},
],
),
).toEqual([]);

expect(
studioFormationSupplementalEntities([], [
{
name: 'Shopify',
kind: 'platform',
officialUrl: 'https://shopify.dev',
docsUrl: 'https://shopify.dev/docs',
timestamps: [],
},
]).map((entity) => entity.name),
).toEqual(['Shopify']);
});

it('tells the truth when events[] is empty after a completed run', () => {
expect(
studioEventsEmptyMessage({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe('UVAI is one product surface', () => {

it('keeps stack-check unlock on the same OneLoopStudio page', () => {
const studio = readSource('components/OneLoopStudio.tsx');
expect(studio).toContain("item.source === 'stack'");
expect(studio).toContain('packFormation.checks');
expect(studio).toContain('setCompletedChecks');
expect(studio).not.toMatch(/window\.location\.(href|assign).*dashboard/);
expect(studio).not.toMatch(/router\.push\(['"`]\/dashboard/);
Expand Down
14 changes: 13 additions & 1 deletion apps/web/src/lib/studio-pipeline-status.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import type { VideoPackCitation } from '@/lib/emit-video-pack';
import { stackChecksFromPackTools, type ChecklistItem } from '@/lib/linked-sop';
import {
stackChecksFromPackTools,
type ChecklistItem,
type LinkedEntity,
} from '@/lib/linked-sop';
import type {
VideoPackArchitecture,
VideoPackArtifact,
Expand Down Expand Up @@ -86,6 +90,14 @@ export function studioPackFormation(pack: VideoPackCitation | null | undefined):
};
}

export function studioFormationSupplementalEntities(
tools: VideoPackStackTool[] | null | undefined,
entities: LinkedEntity[] | null | undefined,
): LinkedEntity[] {
if ((tools?.length ?? 0) > 0) return [];
return entities ?? [];
}

export function studioEventsEmptyMessage(input: {
busy: boolean;
hasCompletedRun: boolean;
Expand Down
Loading