python.profile.detect in spec/project-types.json defines the build profile as a root pyproject.toml carrying [project] (or [build-system]), and the lint-only profile as tool config only with no uv.lock.
A virtual uv workspace root matches neither shape. Its root pyproject.toml carries [tool.uv.workspace], [dependency-groups], and [tool.*], but no [project] or [build-system], and it commits a uv.lock. The [project] tables live in the workspace members under subtrees. ESPHome-Config now has this layout (registry change in #1777).
spec/audit.py does not implement this check mechanically, so no automated finding fires today. A manual or agent audit that reads the assert literally would still flag a declared build profile as a mismatch.
The assert, and canonicalPlacement if it needs it, should cover a workspace root whose members carry [project], and count it as the build shape.
Found by the local strict review of #1777.
🤖 Generated with Claude Code
python.profile.detectinspec/project-types.jsondefines the build profile as a rootpyproject.tomlcarrying[project](or[build-system]), and the lint-only profile as tool config only with nouv.lock.A virtual uv workspace root matches neither shape. Its root
pyproject.tomlcarries[tool.uv.workspace],[dependency-groups], and[tool.*], but no[project]or[build-system], and it commits auv.lock. The[project]tables live in the workspace members under subtrees. ESPHome-Config now has this layout (registry change in #1777).spec/audit.pydoes not implement this check mechanically, so no automated finding fires today. A manual or agent audit that reads the assert literally would still flag a declaredbuildprofile as a mismatch.The assert, and
canonicalPlacementif it needs it, should cover a workspace root whose members carry[project], and count it as the build shape.Found by the local strict review of #1777.
🤖 Generated with Claude Code