Guides

Architecture

How the code is organised, what each file is for, and how a request flows through it. The program is about 6,300 lines of standard-library Python (plus a 540-line web page) and about 3,200 lines of tests, with no build step.

The layers

Packages depend only downwards. A test (tests/test_architecture.py) fails if a lower layer imports a higher one, if core imports anything from the command line, HTTP or the browser, or if a root script grows beyond a thin wrapper.

   cli          web          mcpserver        interfaces: each is a way to drive Lore
     \           |           /
      +----------+----------+
       |                     |
   discovery              reviews          infer rules from code / from code review
       \                    /
        +------ core ------+              the domain: what a rule is, how it is stored, chosen and exported

   root scripts (install.py, memorize.py, ...) only call the main() of one interface
  • core imports nothing of Lore's own other than itself.
  • discovery and reviews import core only.
  • cli, web, mcpserver may import all three, and none imports another interface.

The repository at a glance

PathWhat it is
install.py memorize.py train.py discover.py learn.py review.py serve.py mcp_server.pyEntry points: thin wrappers (16 lines or fewer) that call one interface's main(). Their paths are stable, so client configurations and docs can point at them. They find their sibling folders, so there is nothing to install.
core/The domain.
discovery/Infers rules from code and tooling.
reviews/Learns from pull-request review comments; decides whether a repository is yours.
cli/One module per command, plus shared plumbing.
web/The local web UI.
mcpserver/The MCP server.
tests/The test suite (python3 -m unittest discover -s tests).
docs/This site: plain static files for GitHub Pages.
assets/The logo.
README.md, README-overly-explained.md, CONTRIBUTING.md, SECURITY.md, LICENSEShort README, the long single-page guide, how to contribute, how to report a vulnerability, MIT.

core/: the domain

FileResponsibility
models.pyThe vocabulary: Scope (global, section, glob, with level_for(path)), Rule (all the fields from concepts, with JSON round-tripping that preserves unknown keys), Status, Outcome, Level.
store.pyLow-level storage: the data folder (~/.lore or $LORE_HOME), project-name validation, atomic JSON writes (temporary file, then rename) and the cross-process lock helper.
projects.pyProject and the registry of registered projects; finds the project whose root contains a path (the deepest wins); refuses a name used by another folder.
registration.pyPutting a project on the map; the safe automatic registration of git projects (git_root, register_automatically), never touching the home folder, system folders or Lore's own data.
mapper.pyBuilds the structural map (sections, languages, manifests) without reading file contents; section_of(path) is the single definition of a section.
globs.pyGlob matching: **, *, ?, {a,b}, and bare patterns that match anywhere.
repository.pyRuleRepository: the only code that reads or writes rules.json. Adds with dedupe and contradiction detection, edits, approves, tags, pins and deletes, all under a lock. Validates tags.
sanitize.pyHygiene for everything that can reach an agent: hidden-character detection, clean_rule_text, tag and scope validation, reveal and label for showing untrusted text safely.
risk.pyThe attack-pattern screen (check(text) returns reasons).
textutil.pyNormalising, near-duplicate (similar) and contradiction (contradicts) tests, short ids.
search.pyBM25 ranking with identifier splitting, stemming, aliases and the topic vocabulary.
resolver.pyWhich rules apply to which files for which task: eligibility, selection, ordering, skipping what was sent, the held-back list. Also find for topic search.
usage.pyUsageLog: how often each rule was handed to an agent, in its own file with its own lock.
trainer.pyTurns instruction documents into scoped rules; the bulk-import statistics (TrainStats).
export.pyThe static fallback: writes active rules as Copilot instruction files, refusing symlinks and outside paths.
config.pySettings from ~/.lore/config.json with safe defaults.
instructions.pyThe text agents are given about Lore, built for the approval mode.

discovery/: rules from code

FileResponsibility
__init__.pydiscover() and the Discoverer that runs the detectors in order.
detectors.pyThe detectors: EditorConfigDetector, ManifestDetector, StyleDetector, NamingDetector, LanguageMarkerDetector, FrameworkMarkerDetector. Each inspects a codebase and returns candidates.
codebase.pyThe codebase a detector inspects: its files, source texts and manifest facts, each computed lazily once.
scanner.pyWhich files count as the team's own code (not vendored, minified, generated or huge) and how they are read.
ecosystems.pyRegex readers for manifests and linter configs across ecosystems, and the table of about 90 recognised frameworks and tools.
markers.pyThe extension point: FileMarker, OccurrenceMarker, AbsentMarker, ChoiceMarker, Option, and marker_from_dict for JSON.
models.pyThresholds and Candidate.
registry.pyFinds every LANGUAGE in languages/ and every FRAMEWORKS in frameworks/ automatically.
languages/One module per language: c, cpp, csharp, dart, elixir, go, java, javascript, kotlin, lua, php, python, ruby, rust, scala, svelte, swift, typescript, vue (19), plus base.py (Language).
frameworks/One module per language family, 19 frameworks in all, plus base.py (Framework).

reviews/: rules from code review

FileResponsibility
github.pyReads review comments through gh api; parses pull-request specs (12, 15-18); keeps GitHub's author_association.
models.pyReviewComment (with is_team) and Proposal.
text.pyTurns review markdown into comparable words and a comment into a rule-shaped sentence.
cluster.pyFilters comments, groups those asking for the same thing, infers a scope, and builds proposals.
service.pyThe whole pipeline (learn()), shared by the CLI, the MCP tool and the web UI; also the candidate brief for agents.
ledger.pyRemembers which comments were proposed or dismissed, and when review feedback was last read or tried.
trust.pyDecides whether a repository is yours (no remote, or you can push), with a weekly cache and manual overrides.

The interfaces

FileResponsibility
cli/common.pyThe parser, CliError and the @entry decorator that turns it into error: … and exit code 2, project lookup, scope parsing.
cli/install.py memorize.py train.py discover.py learn.py review.pyOne module per command: see the command pages.
web/server.pyHTTP: security checks (token, Host, Origin, content type, size), routing, JSON in and out.
web/api.pyEverything the page can do, with no HTTP in sight, so it is tested directly.
web/runner.pyRuns Lore's own scripts as subprocesses, never through a shell, one at a time, with a timeout.
web/validation.pyRequest validation helpers and the one ApiError type.
web/index.htmlThe single-page UI, read from disk on every request.
mcpserver/protocol.pyGeneric MCP over stdio (JSON-RPC 2.0). Knows nothing about Lore.
mcpserver/tools.pyLore's eight tools, the session object, and create_server().

How a request flows

get_rules

  1. protocol.py reads the line and calls the tool's handler.
  2. tools.get_rules finds the project from the paths (projects.find_for_path), registering a new git project if needed (registration).
  3. resolver.resolve loads active rules through RuleRepository, drops any with hidden characters, keeps those whose scope (models.Scope.level_for, globs, mapper) covers a path, and, for a long list, ranks them against the task (search.rank), skipping what the session already has.
  4. UsageLog.record counts what was sent; tools adds the one-time notes (it may call trust.get and read Ledger), and returns the text.

record_feedback and propose_rules

  1. tools works out the project from the files and marks it as one the agent is working on.
  2. RuleRepository.add validates the text (sanitize) and tags (risk), compares with existing rules for duplicates and contradictions (textutil), and writes under a lock.
  3. tools._activate applies the gate: config (approval mode and limit), the working-project check, trust.get, Rule.risks, contradictions. Passing rules go through set_status(..., auto=True).

Learning from review

  1. reviews.service.learn fetches comments through github.py (the only place that runs gh), records the attempt in the ledger, and filters and groups them (cluster, text).
  2. It returns proposals. The CLI saves them as pending; the MCP tool turns them into a brief for the agent, which proposes the ones it keeps through propose_rules.

The tests

FileCovers
support.pyShared fixtures: a throw-away LORE_HOME, a registered project, a stand-in gh, helpers to run the scripts.
test_architecture.pyThe layering rules and the thin root scripts.
test_core.pyGlobs, mapper, scopes, rules, repository, usage, resolver, export, trainer, projects, storage.
test_discovery.pyThe registry, every marker, frameworks, extensibility.
test_reviews.pyReview text, clustering, the learn command, the ledger.
test_cli.pyThe commands end to end as subprocesses.
test_mcp.pyThe protocol, the tools over the real script, and locate.
test_agent_tools.pySearch, selection, tagging, proposing, discovery and the learn flow through the server.
test_automation.pySettings, automatic registration, server instructions, the one-time notes.
test_install.pyThe installer against fake clients and a fake home.
test_safety.pyHygiene, contradictions, risk patterns, hostile names, export safety.
test_review_fixes.pyRegressions from the code review: the right project, trust, the review reminder, tool schemas.
test_web.pyThe API, the HTTP layer and every tool card.

Design principles

  • Standard library only. No dependencies to install, audit or update.
  • Deterministic at request time. Serving rules involves no model and no network; the agent's judgement is captured once, as tags.
  • One writer per file. RuleRepository owns rules.json, UsageLog owns usage.json, Ledger owns review-ledger.json. Writes are atomic and locked.
  • Untrusted text is never trusted. Anything from a repository, a reviewer or an agent passes through sanitize before it is stored or shown, and is refused rather than repaired.
  • Interfaces stay thin. Behaviour lives in the layers below, so the CLI, the web UI and the MCP server cannot drift apart.