Symptom
Real-eval against agent-brain produced an empty TECH_DEBT.md (header only, no rows). Stage 6 ran in 0.0s and zero LLM cost.
```
[7/9] stage tech_debt starting
[7/9] stage tech_debt done in 0.0s
```
Root cause
`src/designdoc/index/manifests.py:parse_manifests(repo_root)` only looks for `pyproject.toml`, `requirements.txt`, and `package.json` at `repo_root`. Existing unit tests confirm this scope (`tests/unit/test_manifests.py:11-46`).
agent-brain is a Python monorepo:
```
agent-brain/
├── agent-brain-server/
│ ├── pyproject.toml ← Poetry deps live here
│ └── poetry.lock
├── agent-brain-cli/
│ ├── pyproject.toml
│ └── poetry.lock
└── (no top-level pyproject.toml)
```
`parse_manifests()` finds nothing → Stage 6 has no deps to research → empty ledger.
This pattern is extremely common for Python repos: Poetry workspaces, Pants/Bazel monorepos, Cargo-style workspaces, npm/pnpm workspaces all stash per-package manifests in subdirs.
Proposed fix
Recursive-but-bounded manifest discovery:
```python
def parse_manifests(repo_root: Path, max_depth: int = 3) -> list[Dep]:
"""Walk repo_root up to max_depth deep, find every supported manifest,
deduplicate by dep name (first-seen wins), respect exclude rules."""
deps: dict[str, Dep] = {}
for path in _iter_manifests(repo_root, max_depth=max_depth):
for dep in _parse_one(path):
deps.setdefault(dep.name, dep)
return list(deps.values())
```
Notes:
- Honor `DEFAULT_EXCLUDES` so `.venv`, `node_modules`, etc. don't get walked.
- max_depth=3 covers ~99% of monorepos without runaway costs on huge trees.
- First-seen-wins dedup avoids spurious "same dep, two pins" rows for transitive shared libs.
- An optional config knob `tech_debt.manifest_max_depth` for users with deeply nested workspaces.
Tests
- `test_parse_manifests_walks_monorepo_subdirs` — fixture with `workspace/{a,b}/pyproject.toml`, expect both sets of deps.
- `test_parse_manifests_dedupes_repeated_deps` — same dep in two manifests, only one row in output.
- `test_parse_manifests_respects_default_excludes` — manifest inside `.venv` is ignored.
- `test_parse_manifests_max_depth` — deeply nested manifest beyond max_depth is ignored.
Closing criteria
- Re-run agent-brain eval shows TECH_DEBT.md with rows for both Poetry workspaces' deps.
- Existing tests for root-level manifest parsing still pass.
Severity
Medium. The pipeline still completes; only Stage 6's output is empty. But monorepo support is core to "walks any repo" — and most non-trivial Python projects today are some flavor of monorepo.
Symptom
Real-eval against agent-brain produced an empty TECH_DEBT.md (header only, no rows). Stage 6 ran in 0.0s and zero LLM cost.
```
[7/9] stage tech_debt starting
[7/9] stage tech_debt done in 0.0s
```
Root cause
`src/designdoc/index/manifests.py:parse_manifests(repo_root)` only looks for `pyproject.toml`, `requirements.txt`, and `package.json` at `repo_root`. Existing unit tests confirm this scope (`tests/unit/test_manifests.py:11-46`).
agent-brain is a Python monorepo:
```
agent-brain/
├── agent-brain-server/
│ ├── pyproject.toml ← Poetry deps live here
│ └── poetry.lock
├── agent-brain-cli/
│ ├── pyproject.toml
│ └── poetry.lock
└── (no top-level pyproject.toml)
```
`parse_manifests()` finds nothing → Stage 6 has no deps to research → empty ledger.
This pattern is extremely common for Python repos: Poetry workspaces, Pants/Bazel monorepos, Cargo-style workspaces, npm/pnpm workspaces all stash per-package manifests in subdirs.
Proposed fix
Recursive-but-bounded manifest discovery:
```python
def parse_manifests(repo_root: Path, max_depth: int = 3) -> list[Dep]:
"""Walk repo_root up to max_depth deep, find every supported manifest,
deduplicate by dep name (first-seen wins), respect exclude rules."""
deps: dict[str, Dep] = {}
for path in _iter_manifests(repo_root, max_depth=max_depth):
for dep in _parse_one(path):
deps.setdefault(dep.name, dep)
return list(deps.values())
```
Notes:
Tests
Closing criteria
Severity
Medium. The pipeline still completes; only Stage 6's output is empty. But monorepo support is core to "walks any repo" — and most non-trivial Python projects today are some flavor of monorepo.