From 6fd31e197c0adbe20ecc37629513a8cc802da8e2 Mon Sep 17 00:00:00 2001 From: mathias Date: Tue, 2 Jun 2026 11:05:12 +0000 Subject: [PATCH] feat: add use-case engine scaffold (RED) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Engine wires the ports and exposes ProcessNewVideo, the core use case. Returns ErrNotImplemented on purpose so the acceptance suite fails RED — implementing it to make those tests pass is the first build task. Depends only on ports + domain (dependencies point inward). --- internal/usecase/engine.go | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 internal/usecase/engine.go diff --git a/internal/usecase/engine.go b/internal/usecase/engine.go new file mode 100644 index 0000000..f1e69db --- /dev/null +++ b/internal/usecase/engine.go @@ -0,0 +1,47 @@ +// Package usecase holds Tapir's application core: the engine that detects new +// videos, resolves transcripts, summarizes, and delivers to sinks. It depends +// only on internal/ports and internal/domain. +// +// This is a SCAFFOLD. Methods return ErrNotImplemented so the acceptance tests +// in test/acceptance fail RED. Implementing them to make those tests pass is +// the first build task (see the build issue). Behaviour is specified in +// docs/use-cases/*.feature. +package usecase + +import ( + "context" + "errors" + + "gitea.d-ma.be/mathias/tapir/internal/domain" + "gitea.d-ma.be/mathias/tapir/internal/ports" +) + +// ErrNotImplemented marks scaffold methods awaiting implementation. +var ErrNotImplemented = errors.New("not implemented") + +// Engine is the provider- and sink-agnostic core. +type Engine struct { + Source ports.VideoSource + AI ports.Summarizer + Sinks []ports.Sink +} + +// NewEngine wires the engine from its ports. +func NewEngine(src ports.VideoSource, ai ports.Summarizer, sinks ...ports.Sink) *Engine { + return &Engine{Source: src, AI: ai, Sinks: sinks} +} + +// ProcessResult reports what happened for one video. +type ProcessResult struct { + Video domain.Video + Skipped bool + Reason string // set when Skipped (e.g. "no transcript") + Summary *domain.Summary // nil when Skipped +} + +// ProcessNewVideo runs the core use case for a single video: +// resolve transcript -> (summarize -> deliver) | skip. +// See docs/use-cases/summarize_new_video.feature. +func (e *Engine) ProcessNewVideo(ctx context.Context, v domain.Video) (ProcessResult, error) { + return ProcessResult{}, ErrNotImplemented +}