Files
eugen.thaci f66fb5a19a
CI / lint (push) Failing after 35s
CI / quality (push) Successful in 40s
CI / typecheck (push) Failing after 48s
CI / security (push) Failing after 49s
CI / coverage (push) Has been skipped
CI / build (push) Successful in 20s
CI / helm (push) Successful in 34s
CI / benchmark-regression (push) Has been skipped
CI / unit_tests (push) Failing after 2m12s
CI / docker (push) Has been skipped
CI / e2e_tests (push) Failing after 15m52s
CI / integration_tests (push) Failing after 22m11s
CI / status-check (push) Failing after 1s
CI / benchmark-publish (push) Has been cancelled
docs(spec): align ASCII UI tables in specification and related pages
Align Unicode box-drawing blocks and related tables in specification.md,
ADR-044/045/046, and reference pages for consistent MkDocs rendering.

ISSUES CLOSED: #1171
2026-04-03 04:55:21 +00:00

8.0 KiB

Repository Indexing Service

The Repository Indexing Service scans linked repository resources, building a persistent file-level index with language detection, content hashing, and token estimation. ACMS uses this index for efficient context assembly on projects with 10K+ files.

Architecture

resource_id + root_path
       |
       v
  ┌─── RepoIndexingService ─────┐
  │  walk filesystem            │
  │  apply include/exclude globs│
  │  enforce max_file_size      │
  │  enforce max_total_size     │
  │  SHA-256 content hashing    │
  │  extension language detect  │
  │  estimate token counts      │
  └──────────┬──────────────────┘
             v
  ┌─── SQLite persistence ──────┐
  │  repo_indexes table         │
  │  indexed_files table        │
  └─────────────────────────────┘

Domain Models

All models are frozen Pydantic v2 with ULID identifiers and UTC datetimes.

IndexStatus

Enum representing the state of an index:

Value Description
pending Index creation requested but not yet started
indexing Filesystem walk in progress
ready Index complete and available for queries
error Indexing failed; see error_message
stale Source files changed since last index

FileRecord

Per-file metadata stored during indexing:

Field Type Description
path str Relative path from the repository root
content_hash str SHA-256 hex digest of file contents
token_count int Estimated token count (size_bytes // 4)
language str Detected programming language
size_bytes int File size in bytes
last_modified datetime File modification timestamp (UTC)

IndexMetadata

Summary record for a repository index:

Field Type Description
index_id str ULID identifier for this index snapshot
resource_id str ULID of the linked resource
indexed_at datetime When indexing completed (UTC)
file_count int Total files in the index
token_estimate int Sum of all file token counts
primary_language str Most common language by token count (weighted)
status IndexStatus Current index state
error_message `str None`

RepoIndex

Composite object returned by index and refresh operations:

Field Type Description
metadata IndexMetadata Index summary
files tuple[FileRecord, ...] All indexed file records

Service API

RepoIndexingService(session_factory)

Constructor. Accepts a SQLAlchemy session factory (injected via DI container).

index_resource(resource_id, root_path, *, include_globs, exclude_globs, max_file_size, max_total_size) -> RepoIndex

Full index of a filesystem tree.

Parameter Type Default Description
resource_id str required ULID of the resource
root_path str | Path required Absolute path to the repository root
include_globs tuple[str, ...] () Only index files matching these globs (empty = all)
exclude_globs tuple[str, ...] () Skip files matching these globs
max_file_size int | None None Skip files larger than this (bytes); None = no limit
max_total_size int | None None Stop indexing when cumulative size exceeds this; None = no limit

Raises ValueError if resource_id is empty. Raises FileNotFoundError if root_path does not exist.

refresh_index(resource_id, root_path, **kwargs) -> RepoIndex

Incremental refresh. Compares content hashes against the existing index and only re-processes changed files. Falls back to a full index if no prior index exists. Accepts the same keyword arguments as index_resource.

get_index(resource_id) -> RepoIndex | None

Retrieve a full index (metadata + file records) from the database. Returns None if no index exists.

get_index_status(resource_id) -> IndexMetadata | None

Lightweight query returning only the metadata (no file records). Used by agents project show for status display.

remove_index(resource_id) -> bool

Delete all index data for a resource. Returns True if records were deleted, False if no index existed.

cleanup_stale_indexing() -> int

Remove orphan INDEXING rows left by crashed processes. Should be called at application startup. Returns the number of stale rows removed.

Language Detection

Extension-based detection via detect_language(path). Supported mappings:

Extensions Language
.py, .pyi, .pyx python
.ts, .tsx typescript
.js, .jsx, .mjs, .cjs javascript
.rs rust
.java java
.kt, .kts kotlin
.go go
.c, .h c
.cpp, .cc, .cxx, .hpp cpp
.cs csharp
.rb ruby
.php php
.swift swift
.scala scala
.r r
.md, .mdx markdown
.rst restructuredtext
.json json
.yaml, .yml yaml
.toml toml
.xml xml
.html, .htm html
.css, .scss css
.sql sql
.sh, .bash, .zsh shell
.ps1 powershell
.dockerfile dockerfile
.tf terraform
.lua lua
.zig zig
.nim nim
.ex, .exs elixir
.erl erlang
.hs haskell
.ml, .mli ocaml
.clj clojure
.dart dart
.v v
.jl julia
Makefile, makefile, GNUmakefile makefile
Dockerfile, Dockerfile.* dockerfile

Files with unrecognized extensions return "unknown".

Configuration

Indexing behaviour is controlled via the project's ContextConfig and ContextView:

Config Key Model Field Description
context.include_patterns ContextConfig.include_patterns Include globs
context.ignore_patterns ContextConfig.ignore_patterns Exclude globs
context.max_file_size ContextConfig.max_file_size Per-file size limit
context.max_total_size ContextConfig.max_total_size Total index size cap
context.indexing_strategy ContextConfig.indexing_strategy full_text or semantic
context.auto_refresh ContextConfig.auto_refresh Auto-refresh on access

Database Schema

Two tables are added:

repo_indexes

Column Type Constraints
index_id String(26) PK
resource_id String(26) NOT NULL, UNIQUE, INDEXED
indexed_at String(40) NOT NULL (ISO-8601 UTC)
file_count Integer NOT NULL, DEFAULT 0
token_estimate Integer NOT NULL, DEFAULT 0
primary_language String(50) NOT NULL, DEFAULT "unknown"
status String(20) NOT NULL, DEFAULT "pending", CHECK IN (pending, indexing, ready, stale, error)
error_message Text NULLABLE
created_at String(40) NOT NULL (ISO-8601 UTC)

indexed_files

Column Type Constraints
index_id String(26) PK (composite), FK -> repo_indexes.index_id ON DELETE CASCADE, INDEXED
path String(1024) PK (composite)
content_hash String(64) NOT NULL
token_count Integer NOT NULL, DEFAULT 0
size_bytes Integer NOT NULL, DEFAULT 0
language String(50) NOT NULL, DEFAULT "unknown"
last_modified String(40) NOT NULL

Spec References

  • Lines 727-840: agents info repo indexing display
  • Lines 2829-2916: project link-resource triggers indexing
  • Lines 3322-3395: project show displays index status
  • Lines 19719-19727: Project data model index fields
  • Lines 28649-28664: context.* configuration keys