All Recipes

Auditing a brownfield project before the harness (Phase 0)

Phase 0 of the Harness Engineering series — entering an existing (brownfield) project. Before any AGENTS.md or linters, you audit: baseline.md inventories what already exists (structure, boundaries, conventions, test and CI state as a descriptive baseline), and environment.md pins down the stack and a tool-selection table (role → concrete tool) that every later phase consumes. With copyable audit-bootstrap prompts — stack-agnostic, for JS/TS, Python, Go, Java/Kotlin and any other stack.

IntermediateAI DevOps20 minbaseline.md, environment.md, ADR, Claude Code
1

Why audit before the harness: entering a brownfield project

The harness-engineering series is often read as if the project were a blank slate. In practice you inherit an existing repository (brownfield): someone else's code, implicit conventions, maybe already-chaotic agent use 'in chats'. If you slap AGENTS.md and linters onto such a project right away, you build the harness on guesses — pinning down an architecture that isn't in the code and switching on barriers that fail the build on the very first commit. So Phase 0 is not building, it's inventory. First you honestly learn what the repo already contains, and only then build on top of the real state. The phase produces two documents: baseline.md (current state: what exists, the gap to the target, fix priority) and environment.md (the stack and a tool-selection table every later phase consumes). It's the entry point that comes BEFORE context engineering (Phase 1): you can't describe a project's map without reading the project itself.

🟥 Harness on guesses

  • A map of an architecture not in the code
  • Barriers fail the build on the first commit
  • Tools chosen "by convention", not for the stack

🟩 Audit first

  • baseline.md: what the repo actually has
  • environment.md: stack → tool-selection
  • A prioritized start, not "everything at once"
A brownfield entry is about honesty, not optimism. Where data is missing, baseline.md says 'unsure / TBD', not an invented fact: later phases will silently rely on an invented baseline and propagate the mistake.
2

baseline.md: what we inventory

baseline.md describes the project's current state across several axes. For each axis, three columns: what exists now, the gap to the target, the fix priority. The axes: (1) context engineering — is there an AGENTS.md / CLAUDE.md / README with architecture, ADRs, and what important things live OUTSIDE the repo (chats, cloud docs, tickets); (2) deterministic checks — which formatters, linters, type-checkers, module-boundary checks, dead-code finders are already wired in, and how strict the configs are; (3) quality signals in CI; (4) garbage collection — are there regular drift checks; (5) agent workflow — how the team already uses a coding agent (one thread per project or per task, reusable prompts); (6) repo legibility for a new human/agent. About tests: coverage is recorded as a descriptive baseline number ('currently ~40%'), not a target — the audit's job is to measure the signal, not assign it a plan. And a requirement for the document itself: be concrete — not 'linters partially present' but a list of tools and versions pulled from the configs.

The six axes of baseline.md

Context engineering: AGENTS.md/README/ADRs + what lives OUTSIDE the repo
Deterministic checks: formatter/linter/types/boundaries/dead code + strictness
Quality signals in CI (coverage as a descriptive baseline, not a target)
Garbage collection: are there regular drift checks
Agent workflow: one thread per task? reusable prompts?
Repo legibility: is the project understandable from the repo ALONE
Я внедряю harness engineering на этом проекте и сейчас на Phase 0
(аудит существующего проекта перед построением харнеса).

Создай docs/audit/baseline.md, в котором честно описано текущее
состояние проекта по 6 осям. Для каждой укажи 3 колонки:
что есть сейчас / разрыв до целевого состояния / приоритет фикса.

1. Context engineering. Есть ли AGENTS.md / CLAUDE.md / README с
   описанием архитектуры? Есть ли docs/ и ADR? Что важного лежит ВНЕ
   репо (чаты, облачные доки, тикеты), но влияет на решения?

2. Детерминированные проверки. Что уже подключено: форматтеры,
   линтеры, тайп-чекеры, проверки границ модулей, поиск мёртвого
   кода, security-сканеры. Для каждого — уровень строгости конфига.

3. Сигналы качества. Что измеряется в CI? Покрытие тестами зафиксируй
   как ОПИСАТЕЛЬНОЕ число базы, не как цель. Тесты — сигнал здоровья.

4. Garbage collection. Есть ли регулярные автопроверки на дрейф
   документации, устаревшие зависимости, нарушения архитектуры?

5. Workflow агента. Как команда уже использует кодинг-агента: один
   тред на проект или на задачу? Есть ли переиспользуемые промпты?

6. Читаемость репо. Понял бы новый человек или агент в первом
   запуске, что делает проект, прочитав ТОЛЬКО репо? Что неочевидно?

Будь конкретен: перечисляй инструменты и версии из конфигов, а не
"линтеры частично есть". Где данных нет — пиши "не уверен / TBD",
не выдумывай.
baseline.md is a snapshot, not a reform plan. Don't propose 'how to fix' inside it: its job is to record 'as is'. The decision of what to fix and in what order appears on the last step as a prioritized starting point.
3

environment.md: the tool-selection table (role → tool)

environment.md pins down the project's stack and — above all — the tool-selection table that every later phase leans on (and from which the harness 'configurator' is later assembled). First you describe the context: languages and versions, the build/test toolchain, CI/CD (or 'TBD' if none), a background-job scheduler, repo hosting, deployment, LLM access. Then the heart of the phase: each ROLE from the future barriers is matched to a CONCRETE tool for this exact stack. The key is that the role is universal, the tool is not. The table below shows how one role unfolds across stacks: import boundaries are held by dependency-cruiser in JS/TS, import-linter in Python, ArchUnit in Java/Kotlin, depguard in Go; dead code is found by knip / ts-prune in JS/TS and vulture in Python; types are checked by tsc or mypy; cycles by madge. No single stack is declared universal law: you pick the row for your project. During the audit the 'chosen tool' column is filled with a PROPOSAL, not a decision — a human reviews it before mechanization starts. And the choice is recorded in one ADR (0001-tool-selection) with status Proposed.
Role (universal)JS/TSPythonGo · Java/Kotlin
Import / architecture boundariesdependency-cruiser, eslint-plugin-boundariesimport-linterdepguard · ArchUnit
Dead-code finderknip, ts-prunevulturedeadcode · (IDE inspections)
Type checktscmypy, pyrightgo vet · compiler
Dependency cyclesmadgeimport-linter (contracts)compiler (Go) · ArchUnit (JVM)
Pre-commit orchestratorhusky + lint-stagedpre-commit (framework)lefthook (stack-agnostic)
Phase 0, продолжение. Создай docs/audit/environment.md. Этот
документ фиксирует контекст проекта, на который опираются все
следующие фазы.

Опиши контекст:
1. Стек: языки, фреймворки, рантаймы, версии.
2. Build/test-тулчейн: пакетный менеджер, test-runner, builder.
3. CI/CD: где запускается автоматизация (GitHub Actions / GitLab CI /
   Jenkins / ... / ничего — тогда "TBD").
4. Планировщик фоновых задач: где будут жить регулярные "уборщики".
5. Хостинг репо: GitHub / GitLab / self-hosted.
6. Развёртывание: куда катится прод.
7. Доступ к LLM: какие модели/агенты уже использует команда.

В конце добавь раздел "Tool selection for harness components" —
таблицу, где каждой РОЛИ ставится в соответствие КОНКРЕТНЫЙ инструмент
ИМЕННО ДЛЯ ЭТОГО СТЕКА:

| Роль | Выбранный инструмент | Альтернативы рассмотрены | Причина |
|------|----------------------|--------------------------|---------|
| Форматтер кода | ... | ... | ... |
| Линтер общего назначения | ... | ... | ... |
| Линтер границ модулей | ... | ... | ... |
| Поиск мёртвого кода | ... | ... | ... |
| Проверка типов | ... | ... | ... |
| Security/dependency scanner | ... | ... | ... |
| Pre-commit оркестратор | ... | ... | ... |

ВАЖНО: столбец "выбранный инструмент" заполни ПРЕДЛОЖЕНИЕМ, не
решением. Не объявляй инструмент одного стека универсальным законом —
выбирай под наш стек. Я ревьюну до начала механизации.

Затем заведи ADR docs/decisions/0001-tool-selection.md со статусом
Proposed, фиксирующий эти выборы.
Pick a role by the class of bugs it must catch, not by tool hype. If the repo has no import cycles, don't drag in madge for show — an empty barrier only slows the build. One real role with a configured tool beats seven proposed 'for later'.
4

The layer & boundary map for future barriers

While you read the repo, separately note the layers and the boundaries between them — these are exactly what barriers will defend in Phase 3. On a brownfield project there's often no clear layer model: dependencies flow however they grew. The audit's job is not to impose an ideal architecture but to record the observed one and explicitly mark it 'Discovered, not enforced yet'. That's the honest difference: you write 'this is already how it's done', not 'this was perfectly intended'. For each layer, note the dependency directions: what's allowed, what's forbidden. This arrow map is the future boundary-linter config (dependency-cruiser in JS/TS, import-linter in Python, ArchUnit in Java/Kotlin, depguard in Go — the row from your tool-selection table). The most common violation the agent makes 'on the way' is a domain / business-logic layer that reaches straight into infrastructure (the DB, external APIs). Pin that direction in words now, during the audit, so Phase 3 can mechanize it with a check. Where boundaries aren't clear yet, put '?' and a TODO: an incomplete map beats an invented one.
UI / entry points
allowed
Application / use cases
allowed
Domain / business logic
forbidden (common violation)
Infrastructure: API, DB, queues
Don't confuse 'no layer model' with 'no model needed'. A brownfield almost always has implicit layers — nobody just wrote them down. The audit makes them visible; Phase 3 enforces them via the tool from your tool-selection table.
5

Honest scope: audit ≠ rewrite + what comes next

The main trap of Phase 0 is to see problems and immediately rush to fix them. The audit is not refactoring and not building the whole harness at once. The output is exactly two documents (baseline.md, environment.md) plus an ADR proposal for tools — and a prioritized starting point, not a quarter-long plan. The phase's Definition of Done: baseline.md with concrete gaps, environment.md with the tool-selection table, ADR 0001-tool-selection with status Proposed under human review. The harness's honest boundaries overall: it ensures architectural integrity and maintainability, but does NOT validate functional correctness (whether the code does what the user needs stays with the human and product tests) and does NOT replace review. And don't fix everything at once: one real barrier beats a perfect plan. From baseline, pick the 1–3 cheapest and most painful gaps as a start — the rest goes into later phases. Next is Phase 1: context engineering (AGENTS.md, architecture.md, ADRs), which takes both of your audit documents as input.

Definition of Done — Phase 0

baseline.md: 6 axes, concrete tools/versions, honest "TBD"s
environment.md: stack + tool-selection table (role → tool)
ADR 0001-tool-selection with status Proposed under review
A prioritized start: 1–3 gaps, not a quarter-long plan
Refactoring code "on the way" during the audit
Building the whole harness before the first barrier
Assuming the harness validates functional correctness
If the audit reveals more work than expected, that's not a reason to expand Phase 0 but a signal to decompose. Record the findings in baseline and spread them across phases 1–5; trying to close it all inside the audit is the very scope creep the harness guards against.

Result

You've completed the series entry on an existing (brownfield) project: instead of building the harness on guesses, you audited. In hand: baseline.md (current state across six axes, with coverage as a descriptive baseline, not a target), environment.md with the tool-selection table (role → a concrete tool for your stack), ADR 0001 with status Proposed, and a prioritized starting point of 1–3 gaps. The audit is not a rewrite: you recorded what exists and outlined where to start. Next is Phase 1: context engineering (AGENTS.md, ADRs, architecture.md), which takes both audit documents as input.