fix: add missing port/out and adapter/out sources; fix .gitignore and .dockerignore
All checks were successful
Build and publish Docker image / Build and push CPU image (push) Successful in 2m16s
Build and publish Docker image / Build and push GPU image (push) Successful in 3m6s

- .gitignore had bare 'out/' matching source directories; changed to '/out/'
- All 45 files under trueref-domain/port/out and trueref-adapters/.../out
  were silently excluded from the initial commit
- Added .dockerignore to exclude data/, runtime/, logs/ from build context
This commit is contained in:
moze
2026-05-06 01:36:14 +02:00
parent 0a7fc2c84d
commit e7a8aa95fc
47 changed files with 4154 additions and 1 deletions

View File

@@ -0,0 +1,44 @@
package com.trueref.domain.port.out;
import com.trueref.domain.model.Chunk;
import com.trueref.domain.model.ChunkId;
import com.trueref.domain.model.ChunkVersion;
import com.trueref.domain.model.Embedding;
import com.trueref.domain.model.SearchHit;
import com.trueref.domain.model.SearchScope;
import com.trueref.domain.model.VersionId;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
/**
* Combined chunk metadata + vector + lexical store. The Lucene adapter implements this with a
* single index that holds both BM25-tokenized text and an HNSW kNN field.
*/
public interface ChunkStore {
/** Looks up an existing chunk by content hash so callers can avoid re-embedding. */
Optional<Chunk> findByContentHash(String contentHash);
/** Persists a brand-new chunk and its embedding. Idempotent on {@code contentHash}. */
Chunk upsertChunk(Chunk chunk, Embedding embedding);
/** Adds membership rows linking chunks to a version's source files. */
void linkChunks(Collection<ChunkVersion> links);
/** Removes all chunk-version links for the given version. */
void unlinkVersion(VersionId versionId);
/** Returns the set of {@link ChunkId}s reachable from the given version. */
Set<ChunkId> chunkIdsForVersion(VersionId versionId);
/** BM25 lexical search restricted to the given scope. */
List<SearchHit> bm25Search(String queryText, SearchScope scope, int topK);
/** HNSW dense kNN search restricted to the given scope. */
List<SearchHit> denseSearch(float[] queryVector, SearchScope scope, int topK);
/** Forces a Lucene commit. Called at the end of an indexing job. */
void commit();
}

View File

@@ -0,0 +1,24 @@
package com.trueref.domain.port.out;
import java.nio.file.Path;
import java.util.List;
import org.jspecify.annotations.Nullable;
/**
* Parses a single source file into a list of code chunks. Implementations use tree-sitter for
* supported languages and a sliding-window text splitter as fallback.
*/
public interface CodeParser {
/** True if at least one grammar can handle the given file. */
boolean supports(Path file);
List<ParsedChunk> parse(Path file, String repoRelativePath);
record ParsedChunk(
String content,
String language,
@Nullable String symbol,
int startLine,
int endLine) {}
}

View File

@@ -0,0 +1,11 @@
package com.trueref.domain.port.out;
import java.util.Optional;
/** Persistent on-disk cache of embedding vectors keyed by content hash. */
public interface EmbeddingCache {
Optional<float[]> get(String contentHash);
void put(String contentHash, float[] vector);
}

View File

@@ -0,0 +1,13 @@
package com.trueref.domain.port.out;
import java.util.List;
/** Generates dense embedding vectors. Implementations are expected to be batch-friendly. */
public interface EmbeddingService {
/** Embedding dimensionality of the underlying model. */
int dimension();
/** Embeds a batch of texts. Implementations should call out to GPU through a semaphore. */
List<float[]> embed(List<String> texts);
}

View File

@@ -0,0 +1,50 @@
package com.trueref.domain.port.out;
import java.nio.file.Path;
import java.util.List;
import org.jspecify.annotations.Nullable;
/** Git operations the application needs. Implementations: JGit. */
public interface GitClient {
/** Clones a remote repository to a local directory (no-op if it already exists). */
void cloneRepo(String remoteUrl, Path localPath);
/** Runs git fetch on an existing local repository (no-op for non-managed repos that lack a remote). */
void fetch(Path localPath);
/** Lists tags currently present in the local repository. */
List<TagInfo> listTags(Path localPath);
/** Resolves a ref (tag or branch) to its commit SHA. */
String resolveRef(Path localPath, String ref);
/**
* Checks the given ref out into a transient worktree directory the caller is responsible for
* cleaning up. Returns the worktree root.
*/
Path checkoutWorktree(Path repoPath, String ref);
/** Removes a worktree previously created by {@link #checkoutWorktree}. */
void removeWorktree(Path repoPath, Path worktree);
/**
* Returns the list of files changed between two commits, classified by status.
*
* @param baseRef the previously indexed tag/commit (may be null when there is no parent)
*/
List<DiffEntry> diff(Path repoPath, @Nullable String baseRef, String headRef);
record TagInfo(String name, String commitSha, long taggerEpochSeconds) {}
record DiffEntry(String path, @Nullable String oldPath, ChangeType change) {
public enum ChangeType {
ADDED,
MODIFIED,
DELETED,
RENAMED,
COPIED
}
}
}

View File

@@ -0,0 +1,21 @@
package com.trueref.domain.port.out;
import com.trueref.domain.model.IngestionJob;
import com.trueref.domain.model.JobId;
import com.trueref.domain.model.JobLogEvent;
import java.util.function.Consumer;
/**
* In-process pub/sub bus for ingestion observability events. The application publishes; REST/SSE
* adapters subscribe to fan events out to UI clients.
*/
public interface JobEventBus {
void publishJob(IngestionJob job);
void publishLog(JobLogEvent event);
AutoCloseable subscribeJobs(Consumer<IngestionJob> listener);
AutoCloseable subscribeLogs(JobId jobId, Consumer<JobLogEvent> listener);
}

View File

@@ -0,0 +1,41 @@
package com.trueref.domain.port.out;
import com.trueref.domain.model.IngestionJob;
import com.trueref.domain.model.JobId;
import com.trueref.domain.model.JobStage;
import com.trueref.domain.model.JobStatus;
import com.trueref.domain.model.RepositoryId;
import com.trueref.domain.model.VersionId;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import org.jspecify.annotations.Nullable;
/** Persistence SPI for ingestion jobs and their stages. */
public interface JobStore {
IngestionJob save(IngestionJob job);
Optional<IngestionJob> findById(JobId id);
List<IngestionJob> findRunning();
List<IngestionJob> find(
@Nullable RepositoryId repoId, @Nullable VersionId versionId, @Nullable JobStatus status, int limit);
void updateStatus(
JobId id,
JobStatus status,
@Nullable Instant startedAt,
@Nullable Instant finishedAt);
void upsertStage(JobStage stage);
/**
* Marks all RUNNING and QUEUED jobs as FAILED and their RUNNING stages as FAILED.
* Called once on startup to clear jobs that were interrupted by a previous crash or restart.
*
* @return the number of jobs that were transitioned to FAILED
*/
int failStaleJobs(Instant finishedAt);
}

View File

@@ -0,0 +1,47 @@
package com.trueref.domain.port.out;
import com.trueref.domain.model.Repository;
import com.trueref.domain.model.RepositoryId;
import com.trueref.domain.model.Version;
import com.trueref.domain.model.VersionId;
import com.trueref.domain.model.VersionStatus;
import java.util.List;
import java.util.Optional;
import org.jspecify.annotations.Nullable;
/** Persistence SPI for repositories and their versions. */
public interface RepositoryStore {
Repository save(Repository repo);
Optional<Repository> findById(RepositoryId id);
Optional<Repository> findByName(String name);
List<Repository> findAll();
void delete(RepositoryId id);
Version saveVersion(Version version);
Optional<Version> findVersion(VersionId id);
Optional<Version> findVersionByTag(RepositoryId repoId, String tag);
List<Version> findVersionsByRepo(RepositoryId repoId);
List<Version> findVersionsByStatus(@Nullable RepositoryId repoId, VersionStatus status);
void updateVersionStatus(VersionId id, VersionStatus status, @Nullable String errorMessage);
/** Updates {@code chunkCount} and sets {@code indexedAt = now()}. */
void markVersionIndexed(VersionId id, int chunkCount);
/**
* Marks all INDEXING versions as FAILED.
* Called once on startup to clear versions whose indexing job was interrupted.
*
* @return the number of versions transitioned to FAILED
*/
int failStaleIndexingVersions(String errorMessage);
}

View File

@@ -0,0 +1,11 @@
package com.trueref.domain.port.out;
import com.trueref.domain.model.SearchHit;
import java.util.List;
/** Cross-encoder reranker. Re-scores a candidate list against a query. */
public interface RerankerService {
/** Returns the candidates re-sorted by cross-encoder score, with score replaced. */
List<SearchHit> rerank(String query, List<SearchHit> candidates);
}

View File

@@ -0,0 +1,6 @@
/**
* Driven ports — SPIs implemented by adapters (persistence, vector store, embedding service, git,
* parser, etc.) and called by the application layer.
*/
@org.jspecify.annotations.NullMarked
package com.trueref.domain.port.out;