Chapter 07

Project context

An agent that claims a task in the wrong checkout works without the repo's own conventions and skills. This chapter explains how Ledgenter steers every claim toward the right repo and the right orientation — without ever storing a single repo file.

In plain language

How Ledgenter connects the work to the actual code.

When agents work on software, Ledgenter ties each task to the real thing that delivered it — the commit, branch, or pull request. It also knows which code repositories a project is connected to, so an agent can tell whether it's working in the right place.

The payoff: a one-glance answer to "what code delivered this task?", and a fast orientation for an agent dropping into an unfamiliar project.

The division of labor

Call it the CLAUDE.md problem. A repository carries its own working conventions — a CLAUDE.md at the root, procedures under .claude/skills — and the agent host loads those natively from the checkout. They only take effect if the agent is actually sitting inside that repo. A task tracker that hands out cross-repo work can therefore strand an agent: the task says "fix the parser", but the agent is in the wrong directory, so none of the parser repo's instructions are in its context.

The tempting fix — have Ledgenter store and serve those files — is deliberately rejected. A mirrored CLAUDE.md drifts from the real one the moment someone commits; now there are two sources of truth and the stale one is being injected into agents. So Ledgenter never stores, mirrors, or serves repo conventions. The split is strict:

the checkout (agent's machine)                ledgenter (the server)
──────────────────────────────                ──────────────────────────────
CLAUDE.md        how to work this code        projects ↔ repos   which repo the work needs
.claude/skills   repo-local procedures        runs.repository    which repo you're actually in
                                              tasks · briefs     the live cross-repo work state
loaded natively by the agent host —           never mirrors the files on the left —
only when you work inside the repo            it tells you WHICH repo, and WHAT'S going on

Ledgenter owns exactly what files structurally cannot hold: which repository a task's project is linked to (via repo_register and repo_link, chapter 4), which repository the caller's run detected at start (chapter 6), and the live work state across all of them. The repo's CLAUDE.md says how to work its code; Ledgenter says which repo and what's going on. The system's whole job in this chapter is to correlate those two facts and get the agent physically into the right checkout, where the native machinery takes over.

repo_match: are you in the right checkout?

Every task_claim and task_get returns a compact project_context block — roughly a hundred tokens — containing the project header, the project's linked repos (primary first, capped at ten), and a one-word verdict:

repo_matchMeaning
matchYour run's detected repo is one of the project's linked repos. No hint tokens are spent confirming it — the verdict itself is the confirmation.
mismatchYour run detected a different repo than any the project is linked to.
unknownNo repo was detected for your session at all (you started outside any checkout, or you're a hosted agent with no filesystem).
noneThe project has no linked repos — non-code work; the question doesn't apply.

On mismatch or unknown, the claim also carries a structured hints.repo (expected slug, remote URL, default branch) and a prose hints.next written for the agent to act on:

task_claim — mismatch
{ "ok": true, "claimed": true, "task": { ... },
  "project_context": {
    "project": { "key": "parser", ... },
    "repos": [ { "slug": "acme-parser", "role": "primary", ... } ],   // primary first, ≤10
    "repo_match": "mismatch"
  },
  "hints": {
    "repo": { "match": "mismatch", "expected_repo_slug": "acme-parser", ... },
    "next": "This task's project is linked to repo \"acme-parser\" (…) but your
             current run detected a different repo. Work in a checkout of that repo
             so its CLAUDE.md and skills load — or task_release if you can't."
} }

The verdict is advisory, never blocking, and that restraint is load-bearing. The run's repo is detected once, at session start, so it can be stale: an agent that legitimately switches directories mid-session would otherwise be locked out of its own work by a guess. Ledgenter tells the truth it knows and lets the agent decide — work in the right checkout, or task_release the claim back to the pool. (If you do switch repos mid-session, start a fresh session or pass repository_id explicitly on code refs.) whoami participates too: it reports up to five projects linked to the run's current repo, and annotates its ready-task hint when that task's project lives elsewhere — a pointer suppressed for hosted agents, which have no checkout to open.

project_brief: one-call orientation

Being in the right repo answers how to work the code. The other half of landing well is what's going on — and that is project_brief, the one-call orientation bundle. One request returns:

SectionContentsCap (default / max)
HeaderKey, title, status, owner, dates, descriptionfree text truncated
Countsopen_tasks · ready · blocked · in_review · unassigned_ready · done_last_7dsix numbers
Top open tasksHighest priority first10 / 25
Recent decisionsNon-superseded only — current rulings, not history5 / 10
KnowledgeTitles only, never bodies; runbooks first, then freshest5 / 10
Linked reposPrimary first, with open-PR countsall linked
Recent activityProject, its tasks, and their children10 / 25

The design constraint is the context window: every section is capped, every free-text field is truncated, and the whole brief targets ≤ ~900 tokens at default caps. Knowledge entries are deliberately titles-only — the brief is a map, not the territory; an agent follows up with knowledge_search on whatever looks relevant. Implementation-wise it is pure SQL — no embeddings, no external calls — and runs as security invoker: every SELECT inside it executes under the caller's own row-level security, so the brief can never show a caller anything a direct query wouldn't (chapter 5).

Because orientation is needed in different shapes, the same bundle is delivered four ways:

four surfaces, one brief
project_brief(project: "parser")            // the MCP tool — explicit orientation
task_get(task_id, include: ["brief"])      // one-round-trip cold start on a claimed task
ledgenter://project/parser/brief               // MCP resource — hosts can attach it as context
$ ledgenter project brief parser --json        // the CLI, for scripts and humans

The machine-local repo map

The server only ever speaks in remote URLs and slugs — local filesystem paths are deliberately never persisted server-side, a stance the adversarial gates probe directly (chapter 9). But a hint that says "clone https://…" is wasteful on a machine that already has the checkout. The bridge is a small client-side file, ~/.ledgenter/repos.json, mapping the server's own repo identity — the url_fingerprint, e.g. github/org/name (or local/<slug> for remoteless repos) — to an absolute local path:

~/.ledgenter/repos.json
{
  "version": 1,
  "repos": {
    "github/acme/parser": "C:\\dev\\parser",
    "local/scratch-tools": "C:\\dev\\scratch-tools"
  }
}

The map populates itself: whenever an agent runs inside a repo, core records that repo's root after a successful run start (opt out with LEDGENTER_REPO_MAP=0); the explicit ledgenter repo map command does the same on demand. Core then overlays a local_path onto repo rows in responses — task_claim, task_get, project_brief — so the mismatch hint above effectively becomes "open C:\dev\parser" on this machine. Every operation on the map is best-effort and never throws: it is an ergonomic overlay, never control flow.

[§]

Paths never leave the machine. The overlay is added client-side after the response arrives, and the telemetry layer structurally drops every local_path key before anything is persisted — enforcement by shape, not by policy. The adversarial gate suite includes a probe that attempts to leak a local path through telemetry and fails the build if one gets through.

Scoping claims to checkouts

The final piece is keeping wrong-repo claims from happening at all. task_claim accepts a repository_ids filter: claim-next then only considers tasks whose project is linked to one of the given repos — "only claim work I can actually check out". A loop launcher (chapter 6) passes the repos its machine has; ledgenter task claim --mapped-only derives that list from the local repo map automatically, and refuses with a corrective hint if the map is empty or matches no registered repo rather than silently filtering everything out.

A narrowed filter creates a failure mode worth designing against: a launcher with a wrong or empty repo map would find nothing to claim, tick after tick, and look idle forever. So a repo-narrowed empty pool is never silent — it reports what exists outside the filter:

narrowed pool, nothing claimable
{ "ok": true, "claimed": false, "task": null,
  "pool_outside_filter": 7,
  "hint": "0 claimable task(s) within the given repositories; 7 exist tenant-wide.
           Widen repository_ids (is your repo map current?) or drop the filter." }

The distinction matters operationally: zero tasks within the filter and zero tenant-wide means a genuinely quiet workspace; zero within the filter and seven outside means a misconfiguration — and now it looks like one, to the agent and to anyone reading the run record.

[i]

Taught, not just built. Agents learn this entire model in-band: guide('git-and-repos') covers repo registration, code refs, the repo_match verdicts and what to do about each, and the repo map — so an agent that has never seen these docs still recovers correctly from a mismatch.