I am a real person but I'm using Claude to help me draft issues.
Summary
Kodus currently treats every scan target as a single-package repo. Three related symptoms result on real monorepos:
isMonorepo in projectInfo is always false, even when the target is an obvious monorepo (hatch workspaces, pnpm workspaces, turborepo, cargo workspaces, lerna, nx, ...).
- When you scan a subpackage inside a monorepo, checks that legitimately live at the monorepo root (
readme, license, contributing, codeowners, security-policy, ci-config, editorconfig, dep-update-automation, security-scanning, branch-protection, ...) all report missing — because Kodus never looks up.
- When you scan the monorepo root, per-subpackage checks (
test-framework, test-script, lock-file, linter, formatter, ...) report missing — because Kodus only reads the root manifest and never descends into subpackages.
Reproduction
I'm a Vizro maintainer so let's use that (mckinsey/vizro) to illustrate. It's a Python hatch monorepo with 6 sub-packages:
git clone --depth 1 https://github.com/mckinsey/vizro.git
cd vizro
# Symptoms 1 + 3: root scan
bunx @kodus/agent-readiness . --format json --no-web \
| jq '.projectInfo, .results.testing[0], .results.testing[2]'
Expected: isMonorepo: true, test-framework: pass, test-script: pass.
Actual: isMonorepo: false, both fail — despite every subpackage having its own pytest config and hatch env scripts.
# Symptom 2: subpackage scan
bunx @kodus/agent-readiness vizro-core --format json --no-web \
| jq '.results.documentation[0,1,3], .results.security[0]'
Expected: readme, contributing, codeowners, license all pass (they exist at the monorepo root, which is the source-of-truth level for these governance/legal files).
Actual: all four fail.
Root cause
The pillars in src/pillars/ all use fileExists(repoPath, ...) which only inspects the given repoPath. There is no traversal:
- Upwards to find parent-level shared infrastructure when scanning a subpackage.
- Downwards to find per-package config when scanning a monorepo root.
And getProjectInfo() never sets isMonorepo: true — it appears to be a placeholder awaiting the detection logic.
Suggested approach
1. Detect monorepo shape
At scan start, before running any pillar, walk the target one level and check for known monorepo signals:
- Root scan is a monorepo if ≥2 immediate subdirectories each contain a
pyproject.toml, package.json, Cargo.toml, go.mod, or equivalent manifest.
- Also detect explicit workspace declarations:
[tool.hatch.envs.*] referencing subpackage paths (Python / hatch)
workspaces array in root package.json (npm / yarn / pnpm)
[workspace] in root Cargo.toml (Rust)
turbo.json, nx.json, lerna.json, pnpm-workspace.yaml, rush.json (JS ecosystem)
[tool.uv.workspace] in root pyproject.toml (uv workspaces)
- Subpackage-in-monorepo is a subfolder whose nearest
.git ancestor matches the "root scan" heuristic above.
Set projectInfo.isMonorepo, and add projectInfo.workspaceKind ("hatch", "pnpm", "cargo", ...) and projectInfo.packages (discovered subpackage paths).
2. Propagate results
Categorise the 39 criteria into three buckets:
- Repo-scoped (
readme, license, contributing, codeowners, security-policy, ci-config, deploy-pipeline, branch-protection, dep-update-automation, security-scanning, editorconfig, pre-commit-hooks, version-pinned, containerization, ai-context, secrets-detection). These pass if found anywhere from the current scan target up to the git root. Subpackage scans should check parent directories.
- Package-scoped (
test-framework, test-files-exist, test-script, lock-file, linter, formatter, type-checker, api-docs, coverage-config, e2e-tests, no-outdated-deps, dead-code-detection, bundle-analysis, naming-conventions, test-quality). These pass if found in the scan target itself, or — when scanning a monorepo root — in any subpackage. Report which subpackages contributed to the pass.
- Workflow-scoped (
ci-runs-tests, ci-runs-linters, build-automated). These already inspect CI config content; workspace-agnostic today.
3. Message updates
For rewritten passes, make the source clear so users understand why the check flipped:
contributing: "CONTRIBUTING.md not in vizro-core/, found at monorepo root: ../CONTRIBUTING.md"
test-framework: "pytest configured in 5 of 6 subpackages: vizro-core, vizro-ai, vizro-mcp, vizro-dash-components, vizro-experimental"
Monorepos are pretty common in modern Python (uv workspaces, hatch envs, poetry paths), JS (pnpm / turborepo / nx), and Rust (cargo workspaces). The current behaviour makes Kodus report a false picture for any of them — either overwhelmingly worse than reality (subpackage scan) or underwhelmingly good (root scan). For a tool whose value is honest signal to agents about codebase readiness, this is the highest-impact correctness gap I've found so far.
I am a real person but I'm using Claude to help me draft issues.
Summary
Kodus currently treats every scan target as a single-package repo. Three related symptoms result on real monorepos:
isMonorepoinprojectInfois alwaysfalse, even when the target is an obvious monorepo (hatch workspaces, pnpm workspaces, turborepo, cargo workspaces, lerna, nx, ...).readme,license,contributing,codeowners,security-policy,ci-config,editorconfig,dep-update-automation,security-scanning,branch-protection, ...) all report missing — because Kodus never looks up.test-framework,test-script,lock-file,linter,formatter, ...) report missing — because Kodus only reads the root manifest and never descends into subpackages.Reproduction
I'm a Vizro maintainer so let's use that (
mckinsey/vizro) to illustrate. It's a Python hatch monorepo with 6 sub-packages:Expected:
isMonorepo: true,test-framework: pass,test-script: pass.Actual:
isMonorepo: false, both fail — despite every subpackage having its own pytest config and hatch env scripts.Expected:
readme,contributing,codeowners,licenseall pass (they exist at the monorepo root, which is the source-of-truth level for these governance/legal files).Actual: all four fail.
Root cause
The pillars in
src/pillars/all usefileExists(repoPath, ...)which only inspects the givenrepoPath. There is no traversal:And
getProjectInfo()never setsisMonorepo: true— it appears to be a placeholder awaiting the detection logic.Suggested approach
1. Detect monorepo shape
At scan start, before running any pillar, walk the target one level and check for known monorepo signals:
pyproject.toml,package.json,Cargo.toml,go.mod, or equivalent manifest.[tool.hatch.envs.*]referencing subpackage paths (Python / hatch)workspacesarray in rootpackage.json(npm / yarn / pnpm)[workspace]in rootCargo.toml(Rust)turbo.json,nx.json,lerna.json,pnpm-workspace.yaml,rush.json(JS ecosystem)[tool.uv.workspace]in rootpyproject.toml(uv workspaces).gitancestor matches the "root scan" heuristic above.Set
projectInfo.isMonorepo, and addprojectInfo.workspaceKind("hatch","pnpm","cargo", ...) andprojectInfo.packages(discovered subpackage paths).2. Propagate results
Categorise the 39 criteria into three buckets:
readme,license,contributing,codeowners,security-policy,ci-config,deploy-pipeline,branch-protection,dep-update-automation,security-scanning,editorconfig,pre-commit-hooks,version-pinned,containerization,ai-context,secrets-detection). These pass if found anywhere from the current scan target up to the git root. Subpackage scans should check parent directories.test-framework,test-files-exist,test-script,lock-file,linter,formatter,type-checker,api-docs,coverage-config,e2e-tests,no-outdated-deps,dead-code-detection,bundle-analysis,naming-conventions,test-quality). These pass if found in the scan target itself, or — when scanning a monorepo root — in any subpackage. Report which subpackages contributed to the pass.ci-runs-tests,ci-runs-linters,build-automated). These already inspect CI config content; workspace-agnostic today.3. Message updates
For rewritten passes, make the source clear so users understand why the check flipped:
contributing:"CONTRIBUTING.md not in vizro-core/, found at monorepo root: ../CONTRIBUTING.md"test-framework:"pytest configured in 5 of 6 subpackages: vizro-core, vizro-ai, vizro-mcp, vizro-dash-components, vizro-experimental"Monorepos are pretty common in modern Python (uv workspaces, hatch envs, poetry paths), JS (pnpm / turborepo / nx), and Rust (cargo workspaces). The current behaviour makes Kodus report a false picture for any of them — either overwhelmingly worse than reality (subpackage scan) or underwhelmingly good (root scan). For a tool whose value is honest signal to agents about codebase readiness, this is the highest-impact correctness gap I've found so far.