Ports
Two Protocols define everything the core needs from the outside world. Adapters that satisfy them can be swapped freely.
TemplateRepository
python
class TemplateRepository(Protocol):
def get(self, feature: Feature) -> tuple[Rule, ...]: ...
def features(self) -> tuple[Feature, ...]: ...
def version(self) -> str: ...Built-in adapters
FileSystemTemplateRepository(root: Path, version: str)— loads<feature>.gitignorefiles from a directory; caches per feature.InMemoryTemplateRepository(data: dict[Feature, tuple[Rule, ...]], version: str = "test")— for tests and embedded use.
features() is sorted; the generator never relies on insertion order.
RulesTable
python
class RulesTable(Protocol):
def extras_for(self, features: frozenset[Feature]) -> tuple[Rule, ...]: ...
def version(self) -> str: ...extras_for performs subset matching: a rule with feature set S is returned iff S ⊆ features. The result is sorted by (features, pattern).
Built-in adapters
JsonRulesTable.from_file(path: Path)— loads arules_table.json.InMemoryRulesTable(...)— for tests.
Custom adapters
Anything that satisfies the protocols works. Examples:
- A
TemplateRepositorybacked by a remote object store with a content hash asversion(). - A
RulesTablethat reads from a database, pinningversion()to a schema-migration revision.
The core never serializes these objects — it only calls the methods above.