Skip to content

Commit 0632c20

Browse files
author
Reza Ilmi
committed
feat(seed): attach the Sec 2 Camp photo via a photo: mirror tag
The mirror row has no image column, so seeded reflections could never show a picture. A `photo:` tag (same lane as mood: tags) now carries a public asset path from the seed fixture through the snapshot mapper into the capture's dataUrl, which the mirror detail pane already renders as the top section. Ships public/seed/sec2-camp.jpg and tags the Sec 2 Camp demo reflection. Verified end-to-end in a fresh browser via agent-browser (demo sign-in → History → Jul 24 → photo renders above the quote). Claude-Session: https://claude.ai/code/session_01863ZQb27dqWrPfEcjHKcr4
1 parent e276294 commit 0632c20

5 files changed

Lines changed: 37 additions & 2 deletions

File tree

public/seed/sec2-camp.jpg

184 KB
Loading

src/db/seed.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { eq, sql } from 'drizzle-orm'
1616
import type { Mood, VipsClaimStrength, VipsContextType } from '~/agents/tools/schemas'
1717
import { VIPS_DIMENSIONS, type VipsDimension } from '~/data/vips-taxonomy'
1818
import { sgDateKey } from '~/lib/entry-date'
19-
import { mirrorMoodTag } from '~/server/mood-tags'
19+
import { mirrorMoodTag, mirrorPhotoTag } from '~/server/mood-tags'
2020
import { type TenantContext, withStudent } from './client'
2121
import { type CartographerPathway, insertCartographerOutput, upsertVipsPage } from './queries'
2222
import {
@@ -50,6 +50,11 @@ export interface SeedReflectionFixture {
5050
story_reframe?: string
5151
review_status?: SeedMirrorReviewStatus
5252
mood?: Mood
53+
/** Public asset path for a photo attached to the reflection (e.g.
54+
* "/seed/sec2-camp.jpg"). Stored as a `photo:` mirror tag — the mirror
55+
* row has no image column — and surfaced by the snapshot mapper as the
56+
* capture's `dataUrl`. */
57+
photo?: string
5358
created_at: string
5459
}
5560

@@ -259,6 +264,9 @@ export async function seed(): Promise<SeedResult> {
259264
if (r.mood) {
260265
await attachMirrorTag(ctx.db, student.student_id, reflectionId, mirrorMoodTag(r.mood))
261266
}
267+
if (r.photo) {
268+
await attachMirrorTag(ctx.db, student.student_id, reflectionId, mirrorPhotoTag(r.photo))
269+
}
262270
count++
263271
}
264272

src/lib/student-space/backend-snapshot.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { AuthMenuState } from '~/server/auth-menu.handler.server'
55
import type { LoadTrajectoryResult } from '~/server/load-trajectory.handler.server'
66
import type { LoadVipsPagesResult } from '~/server/load-vips-pages.handler.server'
77
import type { WikiSnapshot } from '~/server/load-wiki.handler.server'
8+
import { photoFromMirrorTags } from '~/server/mood-tags'
89

910
export interface StudentSpaceProfileQuote {
1011
id: string
@@ -47,6 +48,9 @@ export interface StudentSpaceReflectionCaptureSnapshot {
4748
* incremental `captures.patch` writes (AskSheet, retry sync) don't carry
4849
* this field; the detail view reads it via optional chaining. */
4950
validation?: string
51+
/** Photo attached to the reflection — a public asset path (seeded via a
52+
* `photo:` mirror tag) or, for local captures, a data URL. */
53+
dataUrl?: string
5054
reframe: {
5155
headline: string
5256
highlightPhrase: string
@@ -295,6 +299,7 @@ export function mapMirrorEntryToReflectionCapture(
295299
kind: 'ask',
296300
text: entry.transcript,
297301
...(entry.title ? { title: entry.title } : {}),
302+
...(photoUrl(entry) ? { dataUrl: photoUrl(entry) as string } : {}),
298303
validation: entry.validation,
299304
reframe: {
300305
headline: entry.story_reframe,
@@ -436,6 +441,10 @@ function mirrorCaptureId(id: number): string {
436441
return `mirror:${id}`
437442
}
438443

444+
function photoUrl(entry: MirrorEntryRow): string | null {
445+
return photoFromMirrorTags(entry.tags)
446+
}
447+
439448
function toEntryDate(value: string): string {
440449
return sgDateKey(value) ?? '1970-01-01'
441450
}

src/server/mood-tags.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,23 @@ export function mirrorMoodTag(mood: Mood): string {
66
return `${MIRROR_MOOD_TAG_PREFIX}${mood}`
77
}
88

9+
// Photo attachment rides the same tag lane — the mirror row has no image
10+
// column. The value is a public asset path or data URL.
11+
export const MIRROR_PHOTO_TAG_PREFIX = 'photo:' as const
12+
13+
export function mirrorPhotoTag(url: string): string {
14+
return `${MIRROR_PHOTO_TAG_PREFIX}${url}`
15+
}
16+
17+
export function photoFromMirrorTags(tags: readonly string[]): string | null {
18+
for (const tag of tags) {
19+
if (!tag.startsWith(MIRROR_PHOTO_TAG_PREFIX)) continue
20+
const url = tag.slice(MIRROR_PHOTO_TAG_PREFIX.length).trim()
21+
if (url.length > 0) return url
22+
}
23+
return null
24+
}
25+
926
export function moodFromMirrorTags(tags: readonly string[]): Mood | null {
1027
for (const tag of tags) {
1128
if (!tag.startsWith(MIRROR_MOOD_TAG_PREFIX)) continue

test/ablation/fixtures/seed-multistudent.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@
7878
"story_reframe": "Joseph's ankle is the piece of camp you can't put down: how fast it happened, the worry, and then the quieter thought — CPR training doesn't cover this. You walked away with a gap you now know exists.",
7979
"review_status": "confirmed",
8080
"created_at": "2026-07-24T06:30:00Z",
81-
"title": "Sec 2 Camp"
81+
"title": "Sec 2 Camp",
82+
"photo": "/seed/sec2-camp.jpg"
8283
},
8384
{
8485
"context_type": "school",

0 commit comments

Comments
 (0)