Skip to content

Stage 6 (tech_debt) skips monorepo targets — manifest detection only checks repo root #51

@RichardHightower

Description

@RichardHightower

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingenhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions