The index-refactor branch contains two interleaved feature streams that are split into smaller PRs for easier review:
- Indexing pipeline refactor — replaces the old
queue.Queue-based system with a typed sequential coordinator - Face detection — new feature: detect faces, cluster them into people, display as a layout
| Branch | Base | Status | Description |
|---|---|---|---|
split/pipeline-core |
main |
✅ done | Core pipeline coordinator + clip→ai rename |
split/faces-db |
split/pipeline-core |
Face database schema and operations | |
split/faces-detection |
split/faces-db |
Face detection pipeline stage + API wiring | |
split/faces-clustering |
split/faces-detection |
Face clustering algorithm and pipeline stage | |
split/faces-render |
split/faces-clustering |
Face layout and photo overlay rendering | |
split/faces-ui |
split/faces-render |
Frontend UI for faces + e2e tests | |
split/tooling |
main |
Dev tooling: Taskfile, VS Code, API scripts |
Base: main
Replaces the old queue.Queue-based indexing with a sequential coordinator. Renames internal/clip → internal/ai.
Files:
internal/ai/client.go,internal/ai/embedding.go— renamed frominternal/clip/internal/image/pipeline/— new package: coordinator, files, metadata, contents, thumbnail, source, progress stages + tests + READMEinternal/task/task.go— typed task constants (INDEX_FILES,INDEX_METADATA,INDEX_CONTENTS) and registryinternal/image/indexContents.go,indexFiles.go,indexMetadata.go— deleted (replaced by pipeline)internal/image/source.go,database.go,search.go— clip→ai rename, queue fields removed, accessor methods addedinternal/collection/collection.go,internal/render/scene.go,internal/scene/sceneSource.go,config.go— clip→ai renameapi.yaml—POST /taskswithforcefield,DELETE /tasks/{id},INDEX_ALLtask typeinternal/openapi/api.gen.go— regeneratedPIPELINE_DESIGN.md— deleted (content merged into pipeline README)
Base: split/pipeline-core
Self-contained database layer for face data. No behavior change visible to users yet.
Files:
db/migrations/000016_faces.{up,down}.sql—facetable:file_id,x/y/w/h,confidence,embedding,person_id+face_countcount column ininfosinternal/image/source.go—ListFaces()accessor onSource
Base: split/faces-db
Adds the INDEX_FACES pipeline stage. After indexing contents, faces can be detected from original files using the AI service.
Files:
internal/image/pipeline/faces.go—RunFaces()stage: sources files missing face data, callsFaceDetector.DetectFaces(), writes to DBinternal/task/task.go— addTypeIndexFaces,NewFacesTask()internal/image/pipeline/coordinator.go— addFaceDetector/MaxFaceFileSize/FaceWorkerstoConfig; addAddFaces(),stagePrioritycase;AddFilesauto-cascade includes facesapi.yaml— addINDEX_FACEStoTaskTypeenum; regenerateinternal/openapi/api.gen.gomain.go—INDEX_FACEScase inPostTasks, face fields inapplyConfig, face task in--scanstartupconfig.go— face detection config fieldsdefaults.yaml— face detection defaults (max_face_file_size,face_workers)
Base: split/faces-detection
Adds the CLUSTER_FACES task that groups detected faces into person identities using k-NN + Chinese Whispers.
Files:
internal/face/types.go—Face,FaceEmbeddingtypesinternal/face/similarity.go—BuildKNNGraph()using cosine distance (k=10 neighbors, threshold 0.6)internal/face/cluster.go—ChineseWhispers()clustering algorithminternal/image/pipeline/clusterfaces.go—RunClusterFaces()stageinternal/task/task.go— addTypeClusterFaces,NewClusterFacesTask()internal/image/pipeline/coordinator.go— addFaceClusterertoConfig; addAddClusterFaces(), coordinator caseapi.yaml— addCLUSTER_FACEStoTaskTypeenum; regenerateinternal/openapi/api.gen.gomain.go—CLUSTER_FACEScase inPostTasksandtaskDisplayOrder
Base: split/faces-clustering
Renders face boxes on photos and adds a Faces layout that groups photos by detected person.
Files:
internal/render/rect.go—Rectutility helpers used for face bounding boxesinternal/render/bitmap.go— face box drawing on photo tilesinternal/render/photo.go— face overlay integration inDraw()internal/render/scene.go— face data onScenestructinternal/scene/sceneSource.go— load person collections as face-grouped scenesinternal/layout/common.go—Faceslayout type constantinternal/layout/faces.go—LayoutFaces(): groups photos byperson_id, renders face crops as thumbnailsinternal/layout/highlights.go— small tweaks needed for face layoutinternal/io/io.go— small interface addition needed by face render pipelinedocs/features/layouts.md— Faces layout documentation
Base: split/faces-render
Frontend UI for triggering face indexing, browsing people, and viewing face detection status.
Files:
ui/src/components/CollectionDebug.vue— "Index Faces" and "Cluster Faces" buttonsui/src/components/CollectionPanel.vue— face collection display in side panelui/src/components/DisplaySettings.vue— face display toggleui/src/components/TaskList.vue— face task display improvementsui/src/api.js— face API calls (postTaskfor faces/clustering)ui/src/App.vue— face panel integratione2e/tests/tasks.feature— e2e scenarios forINDEX_FACESandCLUSTER_FACEStaskse2e/src/fixtures.ts,e2e/src/steps.ts— e2e test support
Base: main
No prerequisites — can be merged at any point.
Dev tooling, IDE config, and changelog entries. No functional changes to the application.
Files:
Taskfile.yml— new task targets (face-related build/run tasks).vscode/settings.json— IDE configuration.github/copilot-instructions.md— updated project instructionsapi.sh— API testing/exploration script.changes/unreleased/Added-20260314-004311.yaml— changelog: pipeline refactor.changes/unreleased/Added-20260314-004317.yaml— changelog: face detection.changes/unreleased/Added-20260314-004324.yaml— changelog: face clustering
main
├── split/pipeline-core ✅
│ └── split/faces-db
│ └── split/faces-detection
│ └── split/faces-clustering
│ └── split/faces-render
│ └── split/faces-ui
└── split/tooling (independent)